pluto/src/kernel/arch/x86/pic.zig

257 lines
8.3 KiB
Zig
Raw Normal View History

// 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);
}
inline 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_DATA_REG else SLAVE_DATA_REG;
const shift = @intCast(u3, irq_num % 8);
const value: u8 = arch.inb(port) | (u8(1) << shift);
arch.outb(port, value);
}
pub fn clearMask(irq_num: u16) void {
const port: u16 = if (irq_num < 8) MASTER_DATA_REG else SLAVE_DATA_REG;
const shift = @intCast(u3, irq_num % 8);
const value: u8 = arch.inb(port) & ~(u8(1) << shift);
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);
}