/* Interrupt handling for CubeCactusOS */
#include "kernel.h"
extern void outb(uint16_t port, uint8_t value);
extern uint8_t inb(uint16_t port);
extern int printf(const char* format, ...);
/* IDT entry structure */
struct idt_entry {
uint16_t offset_low;
uint16_t selector;
uint8_t zero;
uint8_t type_attr;
uint16_t offset_high;
} __attribute__((packed));
/* IDT pointer structure */
struct idt_ptr {
uint16_t limit;
uint32_t base;
} __attribute__((packed));
/* IDT with 256 entries */
static struct idt_entry idt[256];
static struct idt_ptr idtp;
/* PIC ports */
#define PIC1_COMMAND 0x20
#define PIC1_DATA 0x21
#define PIC2_COMMAND 0xA0
#define PIC2_DATA 0xA1
/* PIC initialization */
void init_pic() {
/* ICW1 - Start initialization */
outb(PIC1_COMMAND, 0x11);
outb(PIC2_COMMAND, 0x11);
/* ICW2 - Set vector offsets */
outb(PIC1_DATA, 0x20); /* Master PIC: IRQ 0-7 -> INT 0x20-0x27 */
outb(PIC2_DATA, 0x28); /* Slave PIC: IRQ 8-15 -> INT 0x28-0x2F */
/* ICW3 - Set up cascading */
outb(PIC1_DATA, 0x04); /* Tell master PIC there's a slave at IRQ2 */
outb(PIC2_DATA, 0x02); /* Tell slave PIC its cascade identity */
/* ICW4 - Set mode */
outb(PIC1_DATA, 0x01); /* 8086 mode */
outb(PIC2_DATA, 0x01);
/* Mask all IRQs except IRQ1 (keyboard) */
outb(PIC1_DATA, 0xFD); /* Enable IRQ1 only */
outb(PIC2_DATA, 0xFF); /* Disable all slave IRQs */
}
/* Set IDT gate */
void idt_set_gate(uint8_t num, uint32_t handler, uint16_t selector, uint8_t flags) {
idt[num].offset_low = handler & 0xFFFF;
idt[num].offset_high = (handler >> 16) & 0xFFFF;
idt[num].selector = selector;
idt[num].zero = 0;
idt[num].type_attr = flags;
}
/* External interrupt handlers defined in assembly */
extern void irq1_handler_asm();
/* Initialize IDT */
void init_idt() {
idtp.limit = (sizeof(struct idt_entry) * 256) - 1;
idtp.base = (uint32_t)&idt;
/* Clear IDT */
for (int i = 0; i < 256; i++) {
idt_set_gate(i, 0, 0, 0);
}
/* Set up keyboard interrupt (IRQ1 = INT 0x21) */
idt_set_gate(0x21, (uint32_t)irq1_handler_asm, 0x08, 0x8E);
/* Load IDT */
__asm__ __volatile__("lidt %0" : : "m"(idtp));
}
/* Send End of Interrupt to PIC */
void send_eoi(uint8_t irq) {
if (irq >= 8) {
outb(PIC2_COMMAND, 0x20);
}
outb(PIC1_COMMAND, 0x20);
}
/* Keyboard IRQ handler (called from assembly) */
extern void keyboard_irq_handler();
void irq1_handler() {
keyboard_irq_handler();
send_eoi(1);
}
/* Initialize interrupt system */
void init_interrupts() {
printf(" - Initializing IDT...\n");
init_idt();
printf(" - Initializing PIC...\n");
init_pic();
printf(" - Enabling interrupts...\n");
__asm__ __volatile__("sti");
}