Step 1 of 5 · Reading · ~4 min
Read
System Calls & I/O
Interrupts & I/O
A CPU that has to poll every device in a loop wastes nearly all its time. Hardware instead lets devices interrupt: the device raises a signal, the CPU finishes its current instruction, looks up a handler in the IDT, runs it, and resumes. Between the device and the CPU sits an interrupt controller that decides which request gets through, in what order, and under which conditions. In this lesson your program is the classic PC controller, a pair of cascaded 8259 PICs, and you will see why every OS reprograms it before enabling interrupts.
Vectors: where interrupts go
The IDT (lesson 4) has 256 entries. Intel reserved the first 32 for CPU exceptions: 0 divide error, 6 invalid opcode, 8 double fault, 13 general protection, 14 page fault, and so on. Some exceptions push an error code (8, 10 to 14, 17), most do not. The remaining vectors, 32 to 255, are free for devices and software interrupts.
The two chips
An IBM PC has two 8259s. The master serves IRQ 0 to 7 (0 timer, 1 keyboard, 2 cascade, 3 and 4 serial ports, 6 floppy, 7 printer); the slave serves IRQ 8 to 15 (8 real-time clock, 12 mouse, 14 and 15 IDE disks) and is wired into the master's IRQ 2 line. Each chip has three 8-bit registers:
| Register | Meaning |
|---|---|
| IRR | requests that have arrived |
| ISR | requests currently in service (delivered, not yet acknowledged) |
| IMR | mask: a 1 bit blocks that line |
When a request arrives, the chip picks the highest-priority line (0 highest, then 1, then the whole slave group in the place of IRQ 2, then 3 to 7) that is requested, unmasked, and not blocked by something in service. It moves the request from IRR to ISR and hands the CPU a vector = base + line number. The kernel's handler must finish with an EOI (end of interrupt) command that clears the highest-priority bit in that chip's ISR. Until then, nothing of equal or lower priority is delivered. For an interrupt from the slave the kernel sends EOI to the slave and to the master.
Why remap
At reset the master's base is vector 8, so IRQ 0 (the timer) arrives as vector 8, the double fault exception. Every OS therefore reprograms the PICs during boot, typically to bases 0x20 and 0x28, well clear of the exceptions. Until the kernel unmasks a line, its requests wait in IRR.
A worked trace
Bases 0x20 and 0x28, everything masked after boot.
| Command | Output | Why |
|---|---|---|
UNMASK 0, UNMASK 1 | timer and keyboard enabled | |
RAISE 1 | IRQ1 vec=0x21 keyboard | delivered, ISR bit 1 set |
RAISE 1 | a second keypress waits in IRR: line 1 is in service | |
RAISE 0 | IRQ0 vec=0x20 timer | line 0 outranks line 1, so it preempts the keyboard handler |
EOI M | clears the lowest set ISR bit: the timer's | |
EOI M | IRQ1 vec=0x21 keyboard | clears the keyboard; the waiting keypress is delivered |
A request that waits in IRR is not lost, but it also is not queued twice: two presses while the line is in service become one pending bit.
Slave interrupts also need the cascade open: with UNMASK 12 but IRQ 2 still masked on the master, RAISE 12 stays pending forever. UNMASK 2 releases it, delivered as vector 0x2C.
Common mistakes
- Forgetting EOI: the line stays in service and everything at its level and below is blocked.
- Not unmasking the cascade line (IRQ 2) and wondering why disk or mouse interrupts never arrive.
- Sending EOI only to the master for a slave interrupt.
- Delivering a lower-priority request while a higher-priority one is in service.
Modern hardware
PCs now use the APIC: a local APIC per CPU and I/O APICs that route interrupts to cores, and PCIe devices use MSI, writing the vector number straight to memory. The ideas are the same: mask, priority, acknowledge.
Your exercise
The starter parses commands and prints. You write the controller's delivery rule and EOI.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…