Added the PIC interface

Also added doc comments to functions
This commit is contained in:
ED 2019-06-02 17:14:02 +01:00
parent 144dffe628
commit 1fc534b9a4
5 changed files with 381 additions and 86 deletions

View file

@ -50,8 +50,8 @@ pub const TSS_OFFSET: u16 = 0x28;
const ACCESSED_BIT = 0x01; // 00000001 const ACCESSED_BIT = 0x01; // 00000001
const WRITABLE_BIT = 0x02; // 00000010 const WRITABLE_BIT = 0x02; // 00000010
const DIRECTION_CONFORMING_BIT = 0x04; // 00000100 const DIRECTION_CONFORMING_BIT = 0x04; // 00000100
const EXECUTABLE_BIT = 0x08; // 00001000 const EXECUTABLE_BIT = 0x08; // 00001000
const DESCRIPTOR_BIT = 0x10; // 00010000 const DESCRIPTOR_BIT = 0x10; // 00010000
const PRIVILEGE_RING_0 = 0x00; // 00000000 const PRIVILEGE_RING_0 = 0x00; // 00000000
const PRIVILEGE_RING_1 = 0x20; // 00100000 const PRIVILEGE_RING_1 = 0x20; // 00100000
@ -115,6 +115,9 @@ pub const GdtPtr = packed struct {
base: *GdtEntry, base: *GdtEntry,
}; };
///
/// The TSS entry structure
///
const TtsEntry = packed struct { const TtsEntry = packed struct {
/// Pointer to the previous TSS entry /// Pointer to the previous TSS entry
prev_tss: u32, prev_tss: u32,
@ -240,7 +243,8 @@ var gdt_entries: [NUMBER_OF_ENTRIES]GdtEntry = []GdtEntry {
makeEntry(0, 0, 0, 0), makeEntry(0, 0, 0, 0),
}; };
/// The GDT pointer that the CPU is loaded with that contains the base address of the GDT and the size. /// The GDT pointer that the CPU is loaded with that contains the base address of the GDT and the
/// size.
const gdt_ptr: GdtPtr = GdtPtr { const gdt_ptr: GdtPtr = GdtPtr {
.limit = TABLE_SIZE, .limit = TABLE_SIZE,
.base = &gdt_entries[0], .base = &gdt_entries[0],
@ -277,10 +281,19 @@ var tss: TtsEntry = TtsEntry {
.io_permissions_base_offset = @sizeOf(TtsEntry), .io_permissions_base_offset = @sizeOf(TtsEntry),
}; };
///
/// Set the stack pointer in the TSS entry
///
/// Arguments:
/// IN esp0: u32 - The stack pointer
///
pub fn setTssStack(esp0: u32) void { pub fn setTssStack(esp0: u32) void {
tss.esp0 = esp0; tss.esp0 = esp0;
} }
///
/// Initialise the Global Descriptor table
///
pub fn init() void { pub fn init() void {
// Initiate TSS // Initiate TSS
gdt_entries[TSS_INDEX] = makeEntry(@ptrToInt(&tss), @sizeOf(TtsEntry) - 1, TSS_SEGMENT, 0); gdt_entries[TSS_INDEX] = makeEntry(@ptrToInt(&tss), @sizeOf(TtsEntry) - 1, TSS_SEGMENT, 0);

View file

@ -13,11 +13,13 @@ const TRAP_GATE_16BIT: u4 = 0x7;
const INTERRUPT_GATE_32BIT: u4 = 0xE; const INTERRUPT_GATE_32BIT: u4 = 0xE;
const TRAP_GATE_32BIT: u4 = 0xF; const TRAP_GATE_32BIT: u4 = 0xF;
// Privilege levels
const PRIVILEGE_RING_0: u2 = 0x0; const PRIVILEGE_RING_0: u2 = 0x0;
const PRIVILEGE_RING_1: u2 = 0x1; const PRIVILEGE_RING_1: u2 = 0x1;
const PRIVILEGE_RING_2: u2 = 0x2; const PRIVILEGE_RING_2: u2 = 0x2;
const PRIVILEGE_RING_3: u2 = 0x3; const PRIVILEGE_RING_3: u2 = 0x3;
/// The structure that contains all the information that each IDT entry needs.
const IdtEntry = packed struct { const IdtEntry = packed struct {
/// The lower 16 bits of the base address of the interrupt handler offset. /// The lower 16 bits of the base address of the interrupt handler offset.
base_low: u16, base_low: u16,
@ -44,6 +46,8 @@ const IdtEntry = packed struct {
base_high: u16, base_high: u16,
}; };
/// The IDT pointer structure that contains the pointer to the beginning of the IDT and the number
/// of the table (minus 1). Used to load the IST with LIDT instruction.
pub const IdtPtr = packed struct { pub const IdtPtr = packed struct {
/// The total size of the IDT (minus 1) in bytes. /// The total size of the IDT (minus 1) in bytes.
limit: u16, limit: u16,
@ -52,13 +56,31 @@ pub const IdtPtr = packed struct {
base: *IdtEntry, base: *IdtEntry,
}; };
/// The IDT entry table of NUMBER_OF_ENTRIES entries.
var idt: [NUMBER_OF_ENTRIES]IdtEntry = []IdtEntry{makeEntry(0, 0, 0, 0, 0)} ** NUMBER_OF_ENTRIES; var idt: [NUMBER_OF_ENTRIES]IdtEntry = []IdtEntry{makeEntry(0, 0, 0, 0, 0)} ** NUMBER_OF_ENTRIES;
/// The IDT pointer that the CPU is loaded with that contains the base address of the IDT and the
/// size.
const idt_ptr: IdtPtr = IdtPtr { const idt_ptr: IdtPtr = IdtPtr {
.limit = TABLE_SIZE, .limit = TABLE_SIZE,
.base = &idt[0], .base = &idt[0],
}; };
///
/// Make a IDT entry.
///
/// Arguments:
/// IN base: u32 - The pointer to the interrupt handler.
/// IN selector: u16 - The segment the interrupt is in. This will usually be the
/// kernels code segment.
/// IN gate_type: u4 - The type of interrupt.
/// IN privilege: u2 - What privilege to call the interrupt in. This will usually be
/// the kernel ring level 0.
/// IN present: u1 - Whether a interrupt handler is present to be called..
///
/// Return:
/// A new IDT entry.
///
fn makeEntry(base: u32, selector: u16, gate_type: u4, privilege: u2, present: u1) IdtEntry { fn makeEntry(base: u32, selector: u16, gate_type: u4, privilege: u2, present: u1) IdtEntry {
return IdtEntry { return IdtEntry {
.base_low = @truncate(u16, base), .base_low = @truncate(u16, base),
@ -72,14 +94,30 @@ fn makeEntry(base: u32, selector: u16, gate_type: u4, privilege: u2, present: u1
}; };
} }
///
/// Open a interrupt gate with a given index and a handler to call.
///
/// Arguments:
/// IN index: u8 - The interrupt number to close.
/// IN base: extern fn()void - The function handler for the interrupt.
///
pub fn openInterruptGate(index: u8, base: extern fn()void) void { pub fn openInterruptGate(index: u8, base: extern fn()void) void {
idt[index] = makeEntry(@ptrToInt(base), gdt.KERNEL_CODE_OFFSET, INTERRUPT_GATE_32BIT, PRIVILEGE_RING_0, 1); idt[index] = makeEntry(@ptrToInt(base), gdt.KERNEL_CODE_OFFSET, INTERRUPT_GATE_32BIT, PRIVILEGE_RING_0, 1);
} }
///
/// Close a interrupt gate with a given index
///
/// Arguments:
/// IN index: u8 - The interrupt number to close.
///
pub fn closeInterruptGate(index: u8) void { pub fn closeInterruptGate(index: u8) void {
idt[index] = makeEntry(0, 0, 0, 0, 0); idt[index] = makeEntry(0, 0, 0, 0, 0);
} }
///
/// Initialise the Interrupt descriptor table
///
pub fn init() void { pub fn init() void {
arch.lidt(&idt_ptr); arch.lidt(&idt_ptr);
} }

View file

@ -4,11 +4,13 @@ const panic = @import("../../panic.zig");
const tty = @import("../../tty.zig"); const tty = @import("../../tty.zig");
const idt = @import("idt.zig"); const idt = @import("idt.zig");
const arch = @import("arch.zig"); const arch = @import("arch.zig");
const pic = @import("pic.zig");
const NUMBER_OF_ENTRIES: u16 = 16; const NUMBER_OF_ENTRIES: u16 = 16;
const IRQ_OFFSET: u16 = 32; const IRQ_OFFSET: u16 = 32;
// The external assembly that is fist called to set up the interrupt handler.
extern fn irq0() void; extern fn irq0() void;
extern fn irq1() void; extern fn irq1() void;
extern fn irq2() void; extern fn irq2() void;
@ -26,114 +28,69 @@ extern fn irq13() void;
extern fn irq14() void; extern fn irq14() void;
extern fn irq15() void; extern fn irq15() void;
// Keep track of the number of spurious irq's /// The list of IRQ handlers initialised to unhandled.
var spurious_irq_counter: u32 = 0;
var irq_handlers: [NUMBER_OF_ENTRIES]fn(*arch.InterruptContext)void = []fn(*arch.InterruptContext)void {unhandled} ** NUMBER_OF_ENTRIES; var irq_handlers: [NUMBER_OF_ENTRIES]fn(*arch.InterruptContext)void = []fn(*arch.InterruptContext)void {unhandled} ** NUMBER_OF_ENTRIES;
///
/// A dummy handler that will make a call to panic as it is a unhandled interrupt.
///
/// Arguments:
/// IN context: *arch.InterruptContext - Pointer to the interrupt context containing the
/// contents of the register at the time of the interrupt.
///
fn unhandled(context: *arch.InterruptContext) void { fn unhandled(context: *arch.InterruptContext) void {
const interrupt_num: u8 = @truncate(u8, context.int_num - IRQ_OFFSET); const interrupt_num: u8 = @truncate(u8, context.int_num - IRQ_OFFSET);
panic.panicFmt(null, "Unhandled IRQ number {}", interrupt_num); panic.panicFmt(null, "Unhandled IRQ number {}", interrupt_num);
} }
// TODO Move to PIC ///
fn sendEndOfInterrupt(irq_num: u8) void { /// The IRQ handler that each of the IRQ's will call when a interrupt happens.
if (irq_num >= 8) { ///
arch.outb(0xA0, 0x20); /// Arguments:
} /// IN context: *arch.InterruptContext - Pointer to the interrupt context containing the
/// contents of the register at the time of the interrupt.
arch.outb(0x20, 0x20); ///
}
// TODO Move to PIC
fn spuriousIrq(irq_num: u8) bool {
// Only for IRQ 7 and 15
if(irq_num == 7) {
// Read master ISR
arch.outb(0x20, 0x0B);
const in_service = arch.inb(0x21);
// Check the MSB is zero, if so, then is a spurious irq
if ((in_service & 0x80) == 0) {
spurious_irq_counter += 1;
return true;
}
} else if (irq_num == 15) {
// Read slave ISR
arch.outb(0xA0, 0x0B);
const in_service = arch.inb(0xA1);
// Check the MSB is zero, if so, then is a spurious irq
if ((in_service & 0x80) == 0) {
// Need to send EOI to the master
arch.outb(0x20, 0x20);
spurious_irq_counter += 1;
return true;
}
}
return false;
}
export fn irqHandler(context: *arch.InterruptContext) void { export fn irqHandler(context: *arch.InterruptContext) void {
const irq_num: u8 = @truncate(u8, context.int_num - IRQ_OFFSET); const irq_num: u8 = @truncate(u8, context.int_num - IRQ_OFFSET);
// Make sure it isn't a spurious irq // Make sure it isn't a spurious irq
if (!spuriousIrq(irq_num)) { if (!pic.spuriousIrq(irq_num)) {
irq_handlers[irq_num](context); irq_handlers[irq_num](context);
// Send the end of interrupt command // Send the end of interrupt command
sendEndOfInterrupt(irq_num); pic.sendEndOfInterrupt(irq_num);
} }
} }
///
/// Register a IRQ by setting its interrupt handler to the given function. This will also clear the
/// mask bit in the PIC so interrupts can happen.
///
/// Arguments:
/// IN irq_num: u16 - The IRQ number to register.
///
pub fn registerIrq(irq_num: u16, handler: fn(*arch.InterruptContext)void) void { pub fn registerIrq(irq_num: u16, handler: fn(*arch.InterruptContext)void) void {
irq_handlers[irq_num] = handler; irq_handlers[irq_num] = handler;
clearMask(irq_num); pic.clearMask(irq_num);
} }
///
/// Unregister a IRQ by setting its interrupt handler to the unhandled function call to panic. This
/// will also set the mask bit in the PIC so no interrupts can happen anyway.
///
/// Arguments:
/// IN irq_num: u16 - The IRQ number to unregister.
///
pub fn unregisterIrq(irq_num: u16) void { pub fn unregisterIrq(irq_num: u16) void {
irq_handlers[irq_num] = unhandled; irq_handlers[irq_num] = unhandled;
setMask(irq_num); pic.setMask(irq_num);
}
pub fn setMask(irq_num: u16) void {
// TODO Change to PIC constants
const port: u16 = if (irq_num < 8) 0x20 else 0xA0;
const value = arch.inb(port) | (1 << irq_num);
arch.outb(port, value);
}
pub fn clearMask(irq_num: u16) void {
// TODO Change to PIC constants
const port: u16 = if (irq_num < 8) 0x20 else 0xA0;
const value = arch.inb(port) & ~(1 << irq_num);
arch.outb(port, value);
}
// TODO Move this to the PIC once got one
fn remapIrq() void {
// Initiate
arch.outb(0x20, 0x11);
arch.outb(0xA0, 0x11);
// Offsets
arch.outb(0x21, IRQ_OFFSET);
arch.outb(0xA1, IRQ_OFFSET + 8);
// IRQ lines
arch.outb(0x21, 0x04);
arch.outb(0xA1, 0x02);
// 80x86 mode
arch.outb(0x21, 0x01);
arch.outb(0xA1, 0x01);
// Mask
arch.outb(0x21, 0xFF);
arch.outb(0xA1, 0xFF);
} }
///
/// Initialise the IRQ interrupts by first remapping the port addresses and then opening up all
/// the IDT interrupt gates for each IRQ.
///
pub fn init() void { pub fn init() void {
// Remap the PIC IRQ so not to overlap with other exceptions // Remap the PIC IRQ so not to overlap with other exceptions
remapIrq(); pic.remapIrq();
// Open all the IRQ's // Open all the IRQ's
idt.openInterruptGate(32, irq0); idt.openInterruptGate(32, irq0);

View file

@ -7,6 +7,7 @@ const arch = @import("arch.zig");
const NUMBER_OF_ENTRIES: u16 = 32; const NUMBER_OF_ENTRIES: u16 = 32;
// The external assembly that is fist called to set up the exception handler.
extern fn isr0() void; extern fn isr0() void;
extern fn isr1() void; extern fn isr1() void;
extern fn isr2() void; extern fn isr2() void;
@ -40,6 +41,7 @@ extern fn isr29() void;
extern fn isr30() void; extern fn isr30() void;
extern fn isr31() void; extern fn isr31() void;
/// The exception messaged that is printed when a exception happens
const exception_msg: [NUMBER_OF_ENTRIES][]const u8 = [NUMBER_OF_ENTRIES][]const u8 { const exception_msg: [NUMBER_OF_ENTRIES][]const u8 = [NUMBER_OF_ENTRIES][]const u8 {
"Divide By Zero", "Divide By Zero",
"Single Step (Debugger)", "Single Step (Debugger)",
@ -75,26 +77,57 @@ const exception_msg: [NUMBER_OF_ENTRIES][]const u8 = [NUMBER_OF_ENTRIES][]const
"Reserved" "Reserved"
}; };
// The of exception handlers initialised to unhandled.
var isr_handlers: [NUMBER_OF_ENTRIES]fn(*arch.InterruptContext)void = []fn(*arch.InterruptContext)void{unhandled} ** NUMBER_OF_ENTRIES; var isr_handlers: [NUMBER_OF_ENTRIES]fn(*arch.InterruptContext)void = []fn(*arch.InterruptContext)void{unhandled} ** NUMBER_OF_ENTRIES;
///
/// A dummy handler that will make a call to panic as it is a unhandled exception.
///
/// Arguments:
/// IN context: *arch.InterruptContext - Pointer to the exception context containing the
/// contents of the register at the time of the exception.
///
fn unhandled(context: *arch.InterruptContext) void { fn unhandled(context: *arch.InterruptContext) void {
const interrupt_num = context.int_num; const interrupt_num = context.int_num;
panic.panicFmt(null, "Unhandled exception: {}, number {}", exception_msg[interrupt_num], interrupt_num); panic.panicFmt(null, "Unhandled exception: {}, number {}", exception_msg[interrupt_num], interrupt_num);
} }
///
/// The exception handler that each of the exceptions will call when a exception happens.
///
/// Arguments:
/// IN context: *arch.InterruptContext - Pointer to the exception context containing the
/// contents of the register at the time of the exception.
///
export fn isrHandler(context: *arch.InterruptContext) void { export fn isrHandler(context: *arch.InterruptContext) void {
const isr_num = context.int_num; const isr_num = context.int_num;
isr_handlers[isr_num](context); isr_handlers[isr_num](context);
} }
///
/// Register an exception by setting its exception handler to the given function.
///
/// Arguments:
/// IN irq_num: u16 - The exception number to register.
///
pub fn registerIsr(isr_num: u16, handler: fn(*arch.InterruptContext)void) void { pub fn registerIsr(isr_num: u16, handler: fn(*arch.InterruptContext)void) void {
isr_handlers[isr_num] = handler; isr_handlers[isr_num] = handler;
} }
///
/// Unregister an exception by setting its exception handler to the unhandled function call to
/// panic.
///
/// Arguments:
/// IN irq_num: u16 - The exception number to unregister.
///
pub fn unregisterIsr(isr_num: u16) void { pub fn unregisterIsr(isr_num: u16) void {
isr_handlers[isr_num] = unhandled; isr_handlers[isr_num] = unhandled;
} }
///
/// Initialise the exception and opening up all the IDT interrupt gates for each exception.
///
pub fn init() void { pub fn init() void {
idt.openInterruptGate(0, isr0); idt.openInterruptGate(0, isr0);
idt.openInterruptGate(1, isr1); idt.openInterruptGate(1, isr1);

254
src/kernel/arch/x86/pic.zig Normal file
View file

@ -0,0 +1,254 @@
// Zig version: 0.4.0
const arch = @import("arch.zig");
// Port address for the PIC master and slave registers
const MASTER_COMMAND_REG: u16 = 0x20; // (Write only).
const MASTER_STATUS_REG: u16 = 0x20; // (Read only).
const MASTER_DATA_REG: u16 = 0x21;
const MASTER_INTERRUPT_MASK_REG: u16 = 0x21;
const SLAVE_COMMAND_REG: u16 = 0xA0; // (Write only).
const SLAVE_STATUS_REG: u16 = 0xA0; // (Read only).
const SLAVE_DATA_REG: u16 = 0xA1;
const SLAVE_INTERRUPT_MASK_REG: u16 = 0xA1;
// Initialisation control word 1. Primary control word for initialising the PIC.
// If set, then the PIC expects to receive a initialisation control word 4.
const ICW1_EXPECT_ICW4: u8 = 0x01;
// If set, then there is only one PIC in the system. If not set, then PIC is cascaded with slave
// PIC's and initialisation control word 3 must be sent to the controller.
const ICW1_SINGLE_CASCADE_MODE: u8 = 0x02;
// If set, then the internal CALL address is 4. If not set, then is 8. Usually ignored by x86.
// So default is not set, 0.
const ICW1_CALL_ADDRESS_INTERVAL_4: u8 = 0x04;
// If set, then operating in level triggered mode. If not set, then operating in edge triggered
// mode.
const ICW1_LEVEL_TRIGGER_MODE: u8 = 0x08;
// If set, then the PIC is to be initialised.
const ICW1_INITIALISATION: u8 = 0x10;
// Initialisation control word 2. Map the base address of the interrupt vector table.
// The new port map for the master PIC. IRQs 0-7 mapped to use interrupts 0x20-0x27
const ICW2_MASTER_REMAP_OFFSET: u8 = 0x20;
// The new port map for the slave PIC. IRQs 8-15 mapped to use interrupts 0x28-0x36
const ICW2_SLAVE_REMAP_OFFSET: u8 = 0x28;
// Initialisation control word 3. For Telling the master and slave where the cascading
// interrupts are coming from.
// Tell the slave PIT to send interrupts to the master PIC on IRQ2
const ICW3_SLAVE_IRQ_MAP_TO_MASTER: u8 = 0x02;
// Tell the master PIT to receive interrupts from the slave PIC on IRQ2
const ICW3_MASTER_IRQ_MAP_FROM_SLAVE: u8 = 0x04;
// Initialisation control word 4. Tell the master and slave what mode to operate in.
// If set, then in 80x86 mode. If not set, then in MCS-80/86 mode
const ICW4_80x86_MODE: u8 = 0x01;
// If set, then on last interrupt acknowledge pulse the PIC automatically performs end of
// interrupt operation.
const ICW4_AUTO_END_OF_INTERRUPT: u8 = 0x02;
// Only use if ICW4_BUFFER_MODE is set. If set, then selects master's buffer. If not set then uses
// slave's buffer.
const ICW4_BUFFER_SELECT: u8 = 0x04;
// If set, then PIC operates in buffered mode.
const ICW4_BUFFER_MODE: u8 = 0x08;
// If set, then the the system had many cascaded PIC's. Not supported in x86.
const ICW4_FULLY_NESTED_MODE: u8 = 0x10;
// Operation control word 1. Interrupt masks.
const OCW1_MASK_IRQ0: u8 = 0x01;
const OCW1_MASK_IRQ1: u8 = 0x02;
const OCW1_MASK_IRQ2: u8 = 0x04;
const OCW1_MASK_IRQ3: u8 = 0x08;
const OCW1_MASK_IRQ4: u8 = 0x10;
const OCW1_MASK_IRQ5: u8 = 0x20;
const OCW1_MASK_IRQ6: u8 = 0x40;
const OCW1_MASK_IRQ7: u8 = 0x80;
// Operation control word 2. Primary commands for the PIC.
// Interrupt level 1 upon which the controller must react. Interrupt level for the current interrupt
const OCW2_INTERRUPT_LEVEL_1: u8 = 0x01;
// Interrupt level 2 upon which the controller must react. Interrupt level for the current interrupt
const OCW2_INTERRUPT_LEVEL_2: u8 = 0x02;
// Interrupt level 3 upon which the controller must react. Interrupt level for the current interrupt
const OCW2_INTERRUPT_LEVEL_3: u8 = 0x04;
// The end of interrupt command code.
const OCW2_END_OF_INTERRUPT: u8 = 0x20;
// Select command.
const OCW2_SELECTION: u8 = 0x40;
// Rotation command.
const OCW2_ROTATION: u8 = 0x80;
// Operation control word 3.
// Read the Interrupt Request Register register
const OCW3_READ_IRR: u8 = 0x00;
// Read the In Service Register register.
const OCW3_READ_ISR: u8 = 0x01;
// If set, then bit 0 will be acted on, so read ISR or IRR. If not set, then no action taken.
const OCW3_ACT_ON_READ: u8 = 0x02;
// If set, then poll command issued. If not set, then no pool command issued.
const OCW3_POLL_COMMAND_ISSUED: u8 = 0x04;
// This must be set for all OCW 3.
const OCW3_DEFAULT: u8 = 0x08;
// If set, then the special mask is set. If not set, then resets special mask.
const OCW3_SPECIAL_MASK: u8 = 0x20;
// If set, then bit 5 will be acted on, so setting the special mask. If not set, then no action it
// taken.
const OCW3_ACK_ON_SPECIAL_MASK: u8 = 0x40;
// IRQ's numbers for the PIC.
pub const IRQ_PIT: u8 = 0x00;
pub const IRQ_KEYBOARD: u8 = 0x01;
pub const IRQ_CASCADE_FOR_SLAVE: u8 = 0x02;
pub const IRQ_SERIAL_PORT_2: u8 = 0x03;
pub const IRQ_SERIAL_PORT_1: u8 = 0x04;
pub const IRQ_PARALLEL_PORT_2: u8 = 0x05;
pub const IRQ_DISKETTE_DRIVE: u8 = 0x06;
pub const IRQ_PARALLEL_PORT_1: u8 = 0x07;
pub const IRQ_REAL_TIME_CLOCK: u8 = 0x08;
pub const IRQ_CGA_VERTICAL_RETRACE: u8 = 0x09;
pub const IRQ_AUXILIARY_DEVICE: u8 = 0x0C;
pub const IRQ_FLOATING_POINT_UNIT: u8 = 0x0D;
pub const IRQ_HARD_DISK_CONTROLLER: u8 = 0x0E;
// Keep track of the number of spurious IRQ's
var spurious_irq_counter: u32 = 0;
inline fn sendCommandMaster(cmd: u8) void {
arch.outb(MASTER_COMMAND_REG, cmd);
}
inline fn sendCommandSlave(cmd: u8) void {
arch.outb(SLAVE_COMMAND_REG, cmd);
}
inline fn sendDataMaster(cmd: u8) void {
arch.outb(MASTER_DATA_REG, cmd);
}
inline fn sendDataSlave(cmd: u8) void {
arch.outb(SLAVE_DATA_REG, cmd);
}
inline fn readDataMaster() u8 {
return arch.inb(MASTER_DATA_REG);
}
inline fn readDataSlave() u8 {
return arch.inb(SLAVE_DATA_REG);
}
fn readMasterIrr() u8 {
sendCommandSlave(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_IRR);
return arch.inb(SLAVE_STATUS_REG);
}
inline fn readSlaveIrr() u8 {
sendCommandMaster(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_IRR);
return arch.inb(MASTER_STATUS_REG);
}
inline fn readMasterIsr() u8 {
sendCommandSlave(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_ISR);
return arch.inb(SLAVE_STATUS_REG);
}
inline fn readSlaveIsr() u8 {
sendCommandMaster(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_ISR);
return arch.inb(MASTER_STATUS_REG);
}
pub fn sendEndOfInterrupt(irq_num: u8) void {
if (irq_num >= 8) {
sendCommandSlave(OCW2_END_OF_INTERRUPT);
}
sendCommandMaster(OCW2_END_OF_INTERRUPT);
}
pub fn spuriousIrq(irq_num: u8) bool {
// Only for IRQ 7 and 15
if(irq_num == 7) {
// Read master ISR
// Check the MSB is zero, if so, then is a spurious irq
// This is (1 << irq_num) or (1 << 7) to check if it is set for this IRQ
if ((readMasterIsr() & 0x80) == 0) {
spurious_irq_counter += 1;
return true;
}
} else if (irq_num == 15) {
// Read slave ISR
// Check the MSB is zero, if so, then is a spurious irq
if ((readSlaveIsr() & 0x80) == 0) {
// Need to send EOI to the master
sendCommandMaster(OCW2_END_OF_INTERRUPT);
spurious_irq_counter += 1;
return true;
}
}
return false;
}
pub fn setMask(irq_num: u16) void {
const port: u16 = if (irq_num < 8) MASTER_COMMAND_REG else SLAVE_COMMAND_REG;
const value = arch.inb(port) | (1 << irq_num);
arch.outb(port, value);
}
pub fn clearMask(irq_num: u16) void {
const port: u16 = if (irq_num < 8) MASTER_COMMAND_REG else SLAVE_COMMAND_REG;
const value = arch.inb(port) & ~(1 << irq_num);
arch.outb(port, value);
}
pub fn remapIrq() void {
// Initiate
sendCommandMaster(ICW1_INITIALISATION | ICW1_EXPECT_ICW4);
sendCommandSlave(ICW1_INITIALISATION | ICW1_EXPECT_ICW4);
// Offsets
sendDataMaster(ICW2_MASTER_REMAP_OFFSET);
sendDataSlave(ICW2_SLAVE_REMAP_OFFSET);
// IRQ lines
sendDataMaster(ICW3_MASTER_IRQ_MAP_FROM_SLAVE);
sendDataSlave(ICW3_SLAVE_IRQ_MAP_TO_MASTER);
// 80x86 mode
sendDataMaster(ICW4_80x86_MODE);
sendDataSlave(ICW4_80x86_MODE);
// Mask
arch.outb(0x21, 0xFF);
arch.outb(0xA1, 0xFF);
}