Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Assembly Stubs

The assembly stubs are the bridge between the CPU’s interrupt mechanism and our Rust dispatcher. Every interrupt goes through one of these before hitting Rust code.

The Standard Pattern

All exception handlers follow the same pattern:

extern rust_interrupt_dispatcher

global divide_by_zero_handler
divide_by_zero_handler:
    cli             ; disable interrupts during handler
    pusha           ; save all general purpose registers
    push esp        ; pointer to saved registers (InterruptFrame)
    push 0          ; interrupt number
    call rust_interrupt_dispatcher
    add esp, 8      ; clean up both pushed arguments
    popa            ; restore all registers
    iretd           ; return from interrupt (32-bit)

cli ensures we don’t get interrupted while handling an interrupt. pusha saves all registers onto the stack. At this point esp points directly to our InterruptFrame, so we push it as the second argument. Then we push the interrupt number as the first argument and call the dispatcher.

Error Code Exceptions

Some exceptions push an error code onto the stack before the CPU-saved registers. Double fault (8), General Protection Fault (13), and Page Fault (14) all do this. We need to remove the error code before pusha so the stack layout matches our InterruptFrame:

global double_fault_handler
double_fault_handler:
    cli
    add esp, 4      ; remove error code pushed by CPU
    pusha
    push esp
    push 8
    call rust_interrupt_dispatcher
    add esp, 8
    popa
    iretd

The Keyboard Handler

The keyboard handler follows the same pattern but with sti before iretd to re-enable interrupts after handling:

global kb_pic_handler
extern rust_interrupt_dispatcher
kb_pic_handler:
    cli
    pusha
    push esp        ; InterruptFrame pointer
    push 33         ; keyboard = IRQ1 = interrupt 33 after PIC remapping
    call rust_interrupt_dispatcher
    add esp, 8
    popa
    sti             ; re-enable interrupts
    iretd

The Default Handler

Catches everything not explicitly handled:

global default_interrupt_handler
default_interrupt_handler:
    cli
    pusha
    push esp
    push 0xFF       ; 255 = unknown interrupt sentinel
    call rust_interrupt_dispatcher
    add esp, 8
    popa
    iretd

Why One Extern?

Previously each assembly file had multiple extern declarations pointing to individual Rust functions. Now there’s just one:

extern rust_interrupt_dispatcher

Everything goes through this single entry point. The signal table inside the dispatcher decides what actually runs.