202 lines
5.7 KiB
Zig
202 lines
5.7 KiB
Zig
const std = @import("std");
|
|
const kernel = @import("kernel");
|
|
const x86 = @import("x86");
|
|
|
|
// PIC ports.
|
|
const PIC1_CMD = 0x20;
|
|
const PIC1_DATA = 0x21;
|
|
const PIC2_CMD = 0xA0;
|
|
const PIC2_DATA = 0xA1;
|
|
// PIC commands:
|
|
const ISR_READ = 0x0B; // Read the In-Service Register.
|
|
const ACK = 0x20; // Acknowledge interrupt.
|
|
// Initialization Control Words commands.
|
|
const ICW1_INIT = 0x10;
|
|
const ICW1_ICW4 = 0x01;
|
|
const ICW4_8086 = 0x01;
|
|
// write 0 to wait
|
|
const WAIT_PORT = 0x80;
|
|
// PIT Channels
|
|
const PIT_CHAN0 = 0x40;
|
|
const PIT_CHAN1 = 0x41;
|
|
const PIT_CHAN2 = 0x42;
|
|
const PIT_CMD = 0x43;
|
|
// Interrupt Vector offsets of exceptions.
|
|
const EXCEPTION_0 = 0;
|
|
const EXCEPTION_31 = EXCEPTION_0 + 31;
|
|
// Interrupt Vector offsets of IRQs.
|
|
const IRQ_0 = EXCEPTION_31 + 1;
|
|
const IRQ_15 = IRQ_0 + 15;
|
|
// Interrupt Vector offsets of syscalls.
|
|
const SYSCALL = 128;
|
|
|
|
// Registered interrupt handlers. (see x86.isr.s)
|
|
const handlers = [_]fn () void{unhandled} ** 48;
|
|
// Registered IRQ subscribers. (see x86.isr.s)
|
|
// var irq_subscribers = []MailboxId{MailboxId.Kernel} ** 16;
|
|
|
|
fn unhandled() noreturn {
|
|
const n = x86.isr.context.interrupt_n;
|
|
kernel.vga.print("unhandled interrupt number {d}", .{n});
|
|
if (n < IRQ_0) kernel.vga.println(" (exception)", .{});
|
|
if (n >= IRQ_0) kernel.vga.println(" (IRQ number {d})", .{n - IRQ_0});
|
|
x86.instr.hang();
|
|
}
|
|
|
|
inline fn picwait() void {
|
|
x86.io.outb(WAIT_PORT, 0);
|
|
}
|
|
|
|
////
|
|
// Call the correct handler based on the interrupt number.
|
|
//
|
|
export fn interruptDispatch() void {
|
|
const n = @intCast(u8, x86.isr.context.interrupt_n);
|
|
|
|
switch (n) {
|
|
// Exceptions.
|
|
EXCEPTION_0...EXCEPTION_31 => {
|
|
kernel.vga.println("", .{});
|
|
kernel.vga.println("num: {}", .{n});
|
|
kernel.vga.println("err: {}", .{@truncate(u8, x86.isr.context.error_code)});
|
|
kernel.vga.println("ip: 0x{x}", .{@truncate(u16, x86.isr.context.eip)});
|
|
kernel.vga.println("ip: 0x{x}", .{@truncate(u16, x86.isr.context.eip >> 16)});
|
|
return handlers[n]();
|
|
},
|
|
|
|
// IRQs.
|
|
IRQ_0...IRQ_15 => {
|
|
const irq = n - IRQ_0;
|
|
// if (spuriousIRQ(irq)) return;
|
|
|
|
startOfInterrupt(irq);
|
|
handlers[n]();
|
|
endOfInterrupt(irq);
|
|
},
|
|
|
|
// Syscalls.
|
|
// SYSCALL => {
|
|
// const syscall_n = x86.isr.context.registers.eax;
|
|
// if (syscall_n < syscall.handlers.len) {
|
|
// syscall.handlers[syscall_n]();
|
|
// } else {
|
|
// syscall.invalid();
|
|
// }
|
|
// },
|
|
|
|
else => unreachable,
|
|
}
|
|
|
|
// If no user thread is ready to run, halt here and wait for interrupts.
|
|
// if (scheduler.current() == null) {
|
|
// sti();
|
|
// hlt();
|
|
// }
|
|
}
|
|
|
|
inline fn spuriousIRQ(irq: u8) bool {
|
|
// Only IRQ 7 and IRQ 15 can be spurious.
|
|
if (irq != 7) return false;
|
|
// TODO: handle spurious IRQ15.
|
|
|
|
// Read the value of the In-Service Register.
|
|
x86.io.outb(PIC1_CMD, ISR_READ);
|
|
const in_service = x86.io.inb(PIC1_CMD);
|
|
|
|
// Verify whether IRQ7 is set in the ISR.
|
|
return (in_service & (1 << 7)) == 0;
|
|
}
|
|
|
|
inline fn startOfInterrupt(irq: u8) void {
|
|
// mask the irq and then ACK
|
|
if (irq >= 8) {
|
|
maskIRQ(irq, true);
|
|
x86.io.outb(PIC1_CMD, ACK);
|
|
x86.io.outb(PIC2_CMD, ACK);
|
|
} else {
|
|
maskIRQ(irq, true);
|
|
x86.io.outb(PIC1_CMD, ACK);
|
|
}
|
|
}
|
|
|
|
inline fn endOfInterrupt(irq: u8) void {
|
|
// unmask the irq and then ACK
|
|
if (irq >= 8) {
|
|
maskIRQ(irq, false);
|
|
x86.io.outb(PIC2_CMD, ACK);
|
|
} else {
|
|
maskIRQ(irq, false);
|
|
x86.io.outb(PIC1_CMD, ACK);
|
|
}
|
|
}
|
|
|
|
pub fn register(n: u8, comptime handler: fn () void) void {
|
|
handlers[n] = handler;
|
|
}
|
|
|
|
pub fn registerIRQ(irq: u8, comptime handler: fn () void) void {
|
|
register(IRQ_0 + irq, handler);
|
|
maskIRQ(irq, false); // Unmask the IRQ.
|
|
}
|
|
|
|
pub fn remapPIC() void {
|
|
// ICW1: start initialization sequence.
|
|
x86.io.outb(PIC1_CMD, ICW1_INIT | ICW1_ICW4);
|
|
picwait();
|
|
x86.io.outb(PIC2_CMD, ICW1_INIT | ICW1_ICW4);
|
|
picwait();
|
|
|
|
// ICW2: Interrupt Vector offsets of IRQs.
|
|
x86.io.outb(PIC1_DATA, IRQ_0); // IRQ 0..7 -> Interrupt 32..39
|
|
picwait();
|
|
x86.io.outb(PIC2_DATA, IRQ_0 + 8); // IRQ 8..15 -> Interrupt 40..47
|
|
picwait();
|
|
|
|
// ICW3: IRQ line 2 to connect master to slave PIC.
|
|
x86.io.outb(PIC1_DATA, 1 << 2);
|
|
picwait();
|
|
x86.io.outb(PIC2_DATA, 2);
|
|
picwait();
|
|
|
|
// ICW4: 80x86 mode.
|
|
x86.io.outb(PIC1_DATA, ICW4_8086);
|
|
picwait();
|
|
x86.io.outb(PIC2_DATA, ICW4_8086);
|
|
picwait();
|
|
|
|
// Mask all IRQs.
|
|
x86.io.outb(PIC1_DATA, 0xFF);
|
|
picwait();
|
|
x86.io.outb(PIC2_DATA, 0xFF);
|
|
picwait();
|
|
}
|
|
|
|
pub fn maskIRQ(irq: u8, comptime mask: bool) void {
|
|
if (irq > 15) return;
|
|
// Figure out if master or slave PIC owns the IRQ.
|
|
const port = @as(u16, if (irq < 8) PIC1_DATA else PIC2_DATA);
|
|
const old = x86.io.inb(port); // Retrieve the current mask.
|
|
|
|
// Mask or unmask the interrupt.
|
|
const shift = @truncate(u3, irq % 8);
|
|
// const shift = @truncate(u3, if (irq < 8) irq else irq - 8);
|
|
const bit = @as(u8, 1) << shift;
|
|
if (mask) x86.io.outb(port, old | bit);
|
|
if (!mask) x86.io.outb(port, old & ~bit);
|
|
// TODO uncomment
|
|
// const new = x86.io.inb(port); // Retrieve the current mask.
|
|
}
|
|
|
|
// configures the chan0 with a rate generator, which will trigger irq0
|
|
pub const divisor = 2685;
|
|
pub const tick = 2251; // f = 1.193182 MHz, TODO: turn into a function
|
|
pub fn configPIT() void {
|
|
const chanNum = 0;
|
|
// const chan = PIT_CHAN0;
|
|
const LOHI = 0b11; // bit4 | bit5
|
|
const PITMODE_RATE_GEN = 0x2;
|
|
|
|
x86.io.outb(PIT_CMD, chanNum << 6 | LOHI << 4 | PITMODE_RATE_GEN << 1);
|
|
x86.io.outb(PIT_CHAN0, divisor & 0xff);
|
|
x86.io.outb(PIT_CHAN0, divisor >> 8);
|
|
}
|