Lesson 35 of 48 intermediate

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

Software interrupts are like calling room service from your hotel phone (INT n) — you initiate the call deliberately from your program. Hardware interrupts are like the fire alarm going off (INTR/NMI) — the external world forces the CPU to respond. The fire alarm (NMI) cannot be ignored no matter what. The room service phone (INTR) can be silenced with a 'Do Not Disturb' sign (CLI/IF=0). Both lead to someone showing up at your door (ISR), but one you requested and one you did not.

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

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 MAIN

Line-by-line walkthrough

  1. 1. PUSHF / POP AX — copies FLAGS into AX so we can examine individual flag bits
  2. 2. TEST AX, 0200h — bit 9 of FLAGS is the IF (Interrupt Flag). TEST performs AND without storing the result, just setting ZF
  3. 3. JZ if_clear_1 — if the AND result is zero, IF was 0 (interrupts disabled). Otherwise IF is 1 (normal state)
  4. 4. CLI before IVT modification — disable hardware interrupts so no device can use a partially written vector
  5. 5. PUSH ES / XOR AX, AX / MOV ES, AX — save ES then point it to segment 0 for IVT access
  6. 6. MOV WORD PTR ES:[180h]... — write our handler's address. 60h * 4 = 180h for the offset, 182h for the segment
  7. 7. POP ES / STI — restore ES and re-enable hardware interrupts. The vector is now safely installed
  8. 8. CLI / INT 60h / STI — key demonstration: CLI disables hardware interrupts, but INT 60h (software) executes normally
  9. 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. 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 ENDP
Need a hint?
How should an ISR return? And is DS set up correctly to access 'counter'?
Show answer
Bug 1: The ISR uses RET instead of IRET. Since the interrupt pushes FLAGS, CS, and IP, RET only pops IP (or IP+CS for FAR RET), leaving FLAGS (and possibly CS) on the stack — corrupting the stack. Fix: use IRET. Bug 2: DS might not point to the data segment when the ISR runs (it could fire at any time). Add PUSH DS / MOV AX, @DATA / MOV DS, AX at the start and POP DS before IRET.

Explain like I'm 5

Imagine you are doing homework (running a program). A software interrupt is like you deciding to get a snack — you choose to stop, go to the kitchen, get your snack, and come back. A hardware interrupt is like your mom calling you for dinner — you did not plan it, but you have to stop and go. NMI is like a fire alarm — you MUST respond no matter what. CLI is like putting on noise-canceling headphones — you will not hear mom (INTR), but you will still hear the fire alarm (NMI), and you can still choose to get a snack (INT n).

Fun fact

In 1988, the Morris Worm — one of the first internet worms — exploited the fact that interrupt handlers on Unix systems could be interrupted themselves. The concept of non-reentrant ISRs (handlers that break when interrupted) has been a source of bugs since the earliest days of computing. The 8086's automatic IF=0 on interrupt entry was Intel's attempt to prevent exactly this class of bug.

Hands-on challenge

Write a program that: (1) installs a custom INT 60h handler that increments a counter, (2) calls INT 60h inside a loop 100 times, (3) verifies the counter equals 100, (4) then disables INTR with CLI and calls INT 60h again to prove software interrupts still work with IF=0, (5) displays 'PASS' or 'FAIL' based on the counter value.

More resources

Open interactive version (quiz + challenge) ← Back to course: Microprocessor A–Z