Merge pull request #34 from SamTebbs33/feature/pic-interface
Added the PIC interface
This commit is contained in:
commit
4b75fb78e5
5 changed files with 381 additions and 86 deletions
|
@ -115,6 +115,9 @@ pub const GdtPtr = packed struct {
|
|||
base: *GdtEntry,
|
||||
};
|
||||
|
||||
///
|
||||
/// The TSS entry structure
|
||||
///
|
||||
const TtsEntry = packed struct {
|
||||
/// Pointer to the previous TSS entry
|
||||
prev_tss: u32,
|
||||
|
@ -240,7 +243,8 @@ var gdt_entries: [NUMBER_OF_ENTRIES]GdtEntry = []GdtEntry {
|
|||
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 {
|
||||
.limit = TABLE_SIZE,
|
||||
.base = &gdt_entries[0],
|
||||
|
@ -277,10 +281,19 @@ var tss: TtsEntry = 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 {
|
||||
tss.esp0 = esp0;
|
||||
}
|
||||
|
||||
///
|
||||
/// Initialise the Global Descriptor table
|
||||
///
|
||||
pub fn init() void {
|
||||
// Initiate TSS
|
||||
gdt_entries[TSS_INDEX] = makeEntry(@ptrToInt(&tss), @sizeOf(TtsEntry) - 1, TSS_SEGMENT, 0);
|
||||
|
|
|
@ -13,11 +13,13 @@ const TRAP_GATE_16BIT: u4 = 0x7;
|
|||
const INTERRUPT_GATE_32BIT: u4 = 0xE;
|
||||
const TRAP_GATE_32BIT: u4 = 0xF;
|
||||
|
||||
// Privilege levels
|
||||
const PRIVILEGE_RING_0: u2 = 0x0;
|
||||
const PRIVILEGE_RING_1: u2 = 0x1;
|
||||
const PRIVILEGE_RING_2: u2 = 0x2;
|
||||
const PRIVILEGE_RING_3: u2 = 0x3;
|
||||
|
||||
/// The structure that contains all the information that each IDT entry needs.
|
||||
const IdtEntry = packed struct {
|
||||
/// The lower 16 bits of the base address of the interrupt handler offset.
|
||||
base_low: u16,
|
||||
|
@ -44,6 +46,8 @@ const IdtEntry = packed struct {
|
|||
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 {
|
||||
/// The total size of the IDT (minus 1) in bytes.
|
||||
limit: u16,
|
||||
|
@ -52,13 +56,31 @@ pub const IdtPtr = packed struct {
|
|||
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;
|
||||
|
||||
/// 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 {
|
||||
.limit = TABLE_SIZE,
|
||||
.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 {
|
||||
return IdtEntry {
|
||||
.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 {
|
||||
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 {
|
||||
idt[index] = makeEntry(0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
///
|
||||
/// Initialise the Interrupt descriptor table
|
||||
///
|
||||
pub fn init() void {
|
||||
arch.lidt(&idt_ptr);
|
||||
}
|
|
@ -4,11 +4,13 @@ const panic = @import("../../panic.zig");
|
|||
const tty = @import("../../tty.zig");
|
||||
const idt = @import("idt.zig");
|
||||
const arch = @import("arch.zig");
|
||||
const pic = @import("pic.zig");
|
||||
|
||||
const NUMBER_OF_ENTRIES: u16 = 16;
|
||||
|
||||
const IRQ_OFFSET: u16 = 32;
|
||||
|
||||
// The external assembly that is fist called to set up the interrupt handler.
|
||||
extern fn irq0() void;
|
||||
extern fn irq1() void;
|
||||
extern fn irq2() void;
|
||||
|
@ -26,114 +28,69 @@ extern fn irq13() void;
|
|||
extern fn irq14() void;
|
||||
extern fn irq15() void;
|
||||
|
||||
// Keep track of the number of spurious irq's
|
||||
var spurious_irq_counter: u32 = 0;
|
||||
|
||||
/// The list of IRQ handlers initialised to unhandled.
|
||||
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 {
|
||||
const interrupt_num: u8 = @truncate(u8, context.int_num - IRQ_OFFSET);
|
||||
panic.panicFmt(null, "Unhandled IRQ number {}", interrupt_num);
|
||||
}
|
||||
|
||||
// TODO Move to PIC
|
||||
fn sendEndOfInterrupt(irq_num: u8) void {
|
||||
if (irq_num >= 8) {
|
||||
arch.outb(0xA0, 0x20);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
///
|
||||
/// The IRQ handler that each of the IRQ's will call when a interrupt happens.
|
||||
///
|
||||
/// Arguments:
|
||||
/// IN context: *arch.InterruptContext - Pointer to the interrupt context containing the
|
||||
/// contents of the register at the time of the interrupt.
|
||||
///
|
||||
export fn irqHandler(context: *arch.InterruptContext) void {
|
||||
const irq_num: u8 = @truncate(u8, context.int_num - IRQ_OFFSET);
|
||||
// Make sure it isn't a spurious irq
|
||||
if (!spuriousIrq(irq_num)) {
|
||||
if (!pic.spuriousIrq(irq_num)) {
|
||||
irq_handlers[irq_num](context);
|
||||
// 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 {
|
||||
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 {
|
||||
irq_handlers[irq_num] = unhandled;
|
||||
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);
|
||||
|
||||
pic.setMask(irq_num);
|
||||
}
|
||||
|
||||
///
|
||||
/// 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 {
|
||||
// Remap the PIC IRQ so not to overlap with other exceptions
|
||||
remapIrq();
|
||||
pic.remapIrq();
|
||||
|
||||
// Open all the IRQ's
|
||||
idt.openInterruptGate(32, irq0);
|
||||
|
|
|
@ -7,6 +7,7 @@ const arch = @import("arch.zig");
|
|||
|
||||
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 isr1() void;
|
||||
extern fn isr2() void;
|
||||
|
@ -40,6 +41,7 @@ extern fn isr29() void;
|
|||
extern fn isr30() 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 {
|
||||
"Divide By Zero",
|
||||
"Single Step (Debugger)",
|
||||
|
@ -75,26 +77,57 @@ const exception_msg: [NUMBER_OF_ENTRIES][]const u8 = [NUMBER_OF_ENTRIES][]const
|
|||
"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;
|
||||
|
||||
///
|
||||
/// 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 {
|
||||
const interrupt_num = context.int_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 {
|
||||
const isr_num = context.int_num;
|
||||
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 {
|
||||
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 {
|
||||
isr_handlers[isr_num] = unhandled;
|
||||
}
|
||||
|
||||
///
|
||||
/// Initialise the exception and opening up all the IDT interrupt gates for each exception.
|
||||
///
|
||||
pub fn init() void {
|
||||
idt.openInterruptGate(0, isr0);
|
||||
idt.openInterruptGate(1, isr1);
|
||||
|
|
254
src/kernel/arch/x86/pic.zig
Normal file
254
src/kernel/arch/x86/pic.zig
Normal 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);
|
||||
}
|
Loading…
Reference in a new issue