From 1fc534b9a41e6a592b28d2d38f36b3200ffb06e8 Mon Sep 17 00:00:00 2001 From: ED Date: Sun, 2 Jun 2019 17:14:02 +0100 Subject: [PATCH] Added the PIC interface Also added doc comments to functions --- src/kernel/arch/x86/gdt.zig | 19 ++- src/kernel/arch/x86/idt.zig | 38 ++++++ src/kernel/arch/x86/irq.zig | 123 ++++++----------- src/kernel/arch/x86/isr.zig | 33 +++++ src/kernel/arch/x86/pic.zig | 254 ++++++++++++++++++++++++++++++++++++ 5 files changed, 381 insertions(+), 86 deletions(-) create mode 100644 src/kernel/arch/x86/pic.zig diff --git a/src/kernel/arch/x86/gdt.zig b/src/kernel/arch/x86/gdt.zig index c0ed63d..b7d8f34 100644 --- a/src/kernel/arch/x86/gdt.zig +++ b/src/kernel/arch/x86/gdt.zig @@ -50,8 +50,8 @@ pub const TSS_OFFSET: u16 = 0x28; const ACCESSED_BIT = 0x01; // 00000001 const WRITABLE_BIT = 0x02; // 00000010 const DIRECTION_CONFORMING_BIT = 0x04; // 00000100 -const EXECUTABLE_BIT = 0x08; // 00001000 -const DESCRIPTOR_BIT = 0x10; // 00010000 +const EXECUTABLE_BIT = 0x08; // 00001000 +const DESCRIPTOR_BIT = 0x10; // 00010000 const PRIVILEGE_RING_0 = 0x00; // 00000000 const PRIVILEGE_RING_1 = 0x20; // 00100000 @@ -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); diff --git a/src/kernel/arch/x86/idt.zig b/src/kernel/arch/x86/idt.zig index 1645238..36376ef 100644 --- a/src/kernel/arch/x86/idt.zig +++ b/src/kernel/arch/x86/idt.zig @@ -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); } \ No newline at end of file diff --git a/src/kernel/arch/x86/irq.zig b/src/kernel/arch/x86/irq.zig index 3b95505..6fe20fd 100644 --- a/src/kernel/arch/x86/irq.zig +++ b/src/kernel/arch/x86/irq.zig @@ -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); diff --git a/src/kernel/arch/x86/isr.zig b/src/kernel/arch/x86/isr.zig index caf3b6f..0bc95d9 100644 --- a/src/kernel/arch/x86/isr.zig +++ b/src/kernel/arch/x86/isr.zig @@ -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); diff --git a/src/kernel/arch/x86/pic.zig b/src/kernel/arch/x86/pic.zig new file mode 100644 index 0000000..4490a4d --- /dev/null +++ b/src/kernel/arch/x86/pic.zig @@ -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); +}