assemblyx86iointerrupt

8259A PIC programming issue?


I want to write my own minimal operating system, and started off with a keyboard driver based on the tutorial of Frank Rosner (https://dev.to/frosnerd/writing-my-own-keyboard-driver-16kh).

Since this requires re-programming of the programmable interrupt controller, and since I'm restricting myself to assembly, I've tried this:

mov eax, 0x11
out 0x20, al

(see also section "8259A Commands" of http://www.brokenthorn.com/Resources/OSDevPic.html)

But when I run the kernel on qemu using

$ qemu-system-i386 -monitor stdio -kernel kernel.elf

the state of the IO port command register is

(qemu) i/i 0x20
portb[0x0020] = 0x1

where it should be 0x11.


Solution

  • The issue stems from how the 8259A Programmable Interrupt Controller (PIC) processes commands. When you write 0x11 to the command register (port 0x20), you're actually initiating the PIC initialization sequence. The value isn't stored in the register; instead, it triggers the initialization process.

    Let's break down what's happening:

    1. You write 0x11 (ICW1: Initialization Command Word 1) to port 0x20

    2. This value contains:

      • Bit 0: Set (1) - Requires ICW4

      • Bit 4: Set (1) - Initialization bit

      • Other bits have specific meanings as well

    3. After receiving this command, the PIC expects a sequence of additional initialization words:

      • ICW2: Base address for IRQs (written to port 0x21)

      • ICW3: Master/slave configuration (written to port 0x21)

      • ICW4: Additional environment info (written to port 0x21)

    The 0x1 you're seeing is likely a status indicator that the PIC is in initialization mode or simply the resulting state after the command processed, not the raw command value you sent.

    For a complete initialization sequence, you need code like this:

    assembly
    
    ; ICW1: Initialize PIC and expect ICW4
    mov al, 0x11
    out 0x20, al    ; Master PIC command
    out 0xA0, al    ; Slave PIC command
    
    ; ICW2: Set base interrupt vectors
    mov al, 0x20    ; Master PIC vectors start at 0x20 (32)
    out 0x21, al
    mov al, 0x28    ; Slave PIC vectors start at 0x28 (40)
    out 0xA1, al
    
    ; ICW3: Setup master/slave relationship
    mov al, 0x04    ; Tell master that slave is at IRQ2 (bit 2)
    out 0x21, al
    mov al, 0x02    ; Tell slave its cascade identity (2)
    out 0xA1, al
    
    ; ICW4: Additional features
    mov al, 0x01    ; 8086/88 mode
    out 0x21, al
    out 0xA1, al
    
    ; Optionally: Mask all interrupts except keyboard (IRQ1)
    mov al, 0xFD    ; 1111 1101 - all bits set except bit 1
    out 0x21, al
    mov al, 0xFF    ; Mask all interrupts on slave PIC
    out 0xA1, al
    

    When working with hardware components like the PIC, commands often trigger state changes rather than simply storing values. The PIC is working as designed - it received your initialization command and changed its state accordingly.

    If you're following Frank Rosner's tutorial, make sure you're implementing the full initialization sequence, not just the first command. Each step is necessary to properly configure the PIC for keyboard interrupt handling.