2019-06-02 18:14:02 +02:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2019-06-26 19:42:38 +02:00
|
|
|
inline fn readMasterIrr() u8 {
|
2019-06-02 18:14:02 +02:00
|
|
|
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 {
|
2019-06-26 19:42:38 +02:00
|
|
|
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);
|
2019-06-02 18:14:02 +02:00
|
|
|
arch.outb(port, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn clearMask(irq_num: u16) void {
|
2019-06-26 19:42:38 +02:00
|
|
|
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);
|
2019-06-02 18:14:02 +02:00
|
|
|
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);
|
|
|
|
}
|