PIC — Advanced Topics
The PIC was covered in depth in KFS_1. This page covers the additions made in KFS_2.
ISR and IRR Registers
The PIC has two status registers useful for debugging:
- IRR (Interrupt Request Register) — Which IRQs have been raised but not yet serviced
- ISR (In-Service Register) — Which IRQs are currently being serviced (sent to CPU)
#![allow(unused)]
fn main() {
const PIC_READ_IRR: u8 = 0x0A;
const PIC_READ_ISR: u8 = 0x0B;
unsafe fn __pic_get_irq_reg(ocw3: u8) -> u16 {
outb(PIC1_COMMAND, ocw3);
outb(PIC2_COMMAND, ocw3);
((inb(PIC2_COMMAND) as u16) << 8) | (inb(PIC1_COMMAND) as u16)
}
unsafe fn pic_get_irr() -> u16 { __pic_get_irq_reg(PIC_READ_IRR) }
unsafe fn pic_get_isr() -> u16 { __pic_get_irq_reg(PIC_READ_ISR) }
}
The combined 16-bit value has PIC2’s 8 bits in the high byte and PIC1’s in the low byte. Bit N corresponds to IRQ N.
Spurious IRQ Handling (Proper)
A spurious IRQ fires when the IRQ line is de-asserted after the PIC has already told the CPU an interrupt is pending. The PIC sends interrupt 7 (master) or interrupt 15 (slave) as a placeholder.
The correct handling:
#![allow(unused)]
fn main() {
fn handle_irq7() {
// Check if it's real or spurious
let isr = unsafe { pic_get_isr() };
if (isr & 0x80) == 0 {
// Bit 7 not set in ISR — it's spurious, don't send EOI
return;
}
// Real IRQ7 — send EOI
pic::send_eoi(7);
}
}
For spurious IRQ15: send EOI to master PIC only (master doesn’t know it’s spurious from the slave).
Mask Helpers
#![allow(unused)]
fn main() {
unsafe fn irq_set_mask(irq_line: u8) {
let (port, line) = if irq_line < 8 {
(PIC1_DATA, irq_line)
} else {
(PIC2_DATA, irq_line - 8)
};
let value = inb(port) | (1 << line);
outb(port, value);
}
unsafe fn irq_clear_mask(irq_line: u8) {
let (port, line) = if irq_line < 8 {
(PIC1_DATA, irq_line)
} else {
(PIC2_DATA, irq_line - 8)
};
let value = inb(port) & !(1 << line);
outb(port, value);
}
}
These allow enabling/disabling individual IRQs without affecting others.