Merge pull request #94 from SamTebbs33/adding-tests-for-pic
Added unit tests for PIC
This commit is contained in:
commit
114f5ea234
7 changed files with 791 additions and 239 deletions
|
@ -3,13 +3,13 @@ const Allocator = std.mem.Allocator;
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const gdt = @import("gdt.zig");
|
const gdt = @import("gdt.zig");
|
||||||
const idt = @import("idt.zig");
|
const idt = @import("idt.zig");
|
||||||
|
const pic = @import("pic.zig");
|
||||||
const irq = @import("irq.zig");
|
const irq = @import("irq.zig");
|
||||||
const isr = @import("isr.zig");
|
const isr = @import("isr.zig");
|
||||||
const pit = @import("pit.zig");
|
const pit = @import("pit.zig");
|
||||||
const paging = @import("paging.zig");
|
const paging = @import("paging.zig");
|
||||||
const syscalls = @import("syscalls.zig");
|
const syscalls = @import("syscalls.zig");
|
||||||
const mem = @import("../../mem.zig");
|
const mem = @import("../../mem.zig");
|
||||||
const log = @import("../../log.zig");
|
|
||||||
const MemProfile = mem.MemProfile;
|
const MemProfile = mem.MemProfile;
|
||||||
|
|
||||||
/// The interrupt context that is given to a interrupt handler. It contains most of the registers
|
/// The interrupt context that is given to a interrupt handler. It contains most of the registers
|
||||||
|
@ -76,15 +76,6 @@ pub fn inb(port: u16) u8 {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// A simple way of waiting for I/O event to happen by doing an I/O event to flush the I/O
|
|
||||||
/// event being waited.
|
|
||||||
///
|
|
||||||
pub fn ioWait() void {
|
|
||||||
// Port 0x80 is free to use
|
|
||||||
outb(0x80, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Load the GDT and refreshing the code segment with the code segment offset of the kernel as we
|
/// Load the GDT and refreshing the code segment with the code segment offset of the kernel as we
|
||||||
/// are still in kernel land. Also loads the kernel data segment into all the other segment
|
/// are still in kernel land. Also loads the kernel data segment into all the other segment
|
||||||
|
@ -230,6 +221,7 @@ pub fn init(mem_profile: *const MemProfile, allocator: *Allocator, comptime opti
|
||||||
gdt.init();
|
gdt.init();
|
||||||
idt.init();
|
idt.init();
|
||||||
|
|
||||||
|
pic.init();
|
||||||
isr.init();
|
isr.init();
|
||||||
irq.init();
|
irq.init();
|
||||||
|
|
||||||
|
@ -245,6 +237,7 @@ pub fn init(mem_profile: *const MemProfile, allocator: *Allocator, comptime opti
|
||||||
test "" {
|
test "" {
|
||||||
_ = @import("gdt.zig");
|
_ = @import("gdt.zig");
|
||||||
_ = @import("idt.zig");
|
_ = @import("idt.zig");
|
||||||
|
_ = @import("pic.zig");
|
||||||
_ = @import("syscalls.zig");
|
_ = @import("syscalls.zig");
|
||||||
_ = @import("paging.zig");
|
_ = @import("paging.zig");
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,9 @@ const builtin = @import("builtin");
|
||||||
const is_test = builtin.is_test;
|
const is_test = builtin.is_test;
|
||||||
|
|
||||||
const build_options = @import("build_options");
|
const build_options = @import("build_options");
|
||||||
const arch = if (is_test) @import(build_options.arch_mock_path ++ "arch_mock.zig") else @import("arch.zig");
|
const mock_path = build_options.arch_mock_path;
|
||||||
const log = if (is_test) @import(build_options.arch_mock_path ++ "log_mock.zig") else @import("../../log.zig");
|
const arch = if (is_test) @import(mock_path ++ "arch_mock.zig") else @import("arch.zig");
|
||||||
|
const log = if (is_test) @import(mock_path ++ "log_mock.zig") else @import("../../log.zig");
|
||||||
|
|
||||||
/// The access bits for a GDT entry.
|
/// The access bits for a GDT entry.
|
||||||
const AccessBits = packed struct {
|
const AccessBits = packed struct {
|
||||||
|
|
|
@ -6,9 +6,10 @@ const builtin = @import("builtin");
|
||||||
const is_test = builtin.is_test;
|
const is_test = builtin.is_test;
|
||||||
|
|
||||||
const build_options = @import("build_options");
|
const build_options = @import("build_options");
|
||||||
const gdt = if (is_test) @import(build_options.arch_mock_path ++ "gdt_mock.zig") else @import("gdt.zig");
|
const mock_path = build_options.arch_mock_path;
|
||||||
const arch = if (is_test) @import(build_options.arch_mock_path ++ "arch_mock.zig") else @import("arch.zig");
|
const gdt = if (is_test) @import(mock_path ++ "gdt_mock.zig") else @import("gdt.zig");
|
||||||
const log = if (is_test) @import(build_options.arch_mock_path ++ "log_mock.zig") else @import("../../log.zig");
|
const arch = if (is_test) @import(mock_path ++ "arch_mock.zig") else @import("arch.zig");
|
||||||
|
const log = if (is_test) @import(mock_path ++ "log_mock.zig") else @import("../../log.zig");
|
||||||
|
|
||||||
/// The structure that contains all the information that each IDT entry needs.
|
/// The structure that contains all the information that each IDT entry needs.
|
||||||
const IdtEntry = packed struct {
|
const IdtEntry = packed struct {
|
||||||
|
@ -326,6 +327,7 @@ fn rt_loadedIDTSuccess() void {
|
||||||
const loaded_idt = arch.sidt();
|
const loaded_idt = arch.sidt();
|
||||||
expect(idt_ptr.limit == loaded_idt.limit);
|
expect(idt_ptr.limit == loaded_idt.limit);
|
||||||
expect(idt_ptr.base == loaded_idt.base);
|
expect(idt_ptr.base == loaded_idt.base);
|
||||||
|
log.logInfo("IDT: Tested loading IDT\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -333,5 +335,4 @@ fn rt_loadedIDTSuccess() void {
|
||||||
///
|
///
|
||||||
fn runtimeTests() void {
|
fn runtimeTests() void {
|
||||||
rt_loadedIDTSuccess();
|
rt_loadedIDTSuccess();
|
||||||
log.logInfo("IDT: Tested loading IDT\n");
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,9 +79,9 @@ fn openIrq(index: u8, handler: idt.InterruptHandler) void {
|
||||||
/// mask bit in the PIC so interrupts can happen.
|
/// mask bit in the PIC so interrupts can happen.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN irq_num: u16 - The IRQ number to register.
|
/// IN irq_num: u8 - The IRQ number to register.
|
||||||
///
|
///
|
||||||
pub fn registerIrq(irq_num: u16, handler: fn (*arch.InterruptContext) void) void {
|
pub fn registerIrq(irq_num: u8, handler: fn (*arch.InterruptContext) void) void {
|
||||||
irq_handlers[irq_num] = handler;
|
irq_handlers[irq_num] = handler;
|
||||||
pic.clearMask(irq_num);
|
pic.clearMask(irq_num);
|
||||||
}
|
}
|
||||||
|
@ -103,9 +103,6 @@ pub fn unregisterIrq(irq_num: u16) void {
|
||||||
/// the IDT interrupt gates for each IRQ.
|
/// 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
|
|
||||||
pic.remapIrq();
|
|
||||||
|
|
||||||
// Open all the IRQ's
|
// Open all the IRQ's
|
||||||
openIrq(32, irq0);
|
openIrq(32, irq0);
|
||||||
openIrq(33, irq1);
|
openIrq(33, irq1);
|
||||||
|
|
|
@ -1,192 +1,362 @@
|
||||||
// Zig version: 0.4.0
|
const std = @import("std");
|
||||||
|
const expect = std.testing.expect;
|
||||||
|
const expectEqual = std.testing.expectEqual;
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const is_test = builtin.is_test;
|
||||||
|
const build_options = @import("build_options");
|
||||||
|
const mock_path = build_options.arch_mock_path;
|
||||||
|
const arch = if (is_test) @import(mock_path ++ "arch_mock.zig") else @import("arch.zig");
|
||||||
|
const log = if (is_test) @import(mock_path ++ "log_mock.zig") else @import("../../log.zig");
|
||||||
|
const panic = if (is_test) @import(mock_path ++ "panic_mock.zig").panic else @import("../../panic.zig").panic;
|
||||||
|
|
||||||
const arch = @import("arch.zig");
|
// ----------
|
||||||
|
// Port address for the PIC master and slave registers.
|
||||||
|
// ----------
|
||||||
|
|
||||||
// Port address for the PIC master and slave registers
|
/// The port address for issuing a command to the master PIC. This is a write only operation.
|
||||||
const MASTER_COMMAND_REG: u16 = 0x20; // (Write only).
|
const MASTER_COMMAND_REG: u16 = 0x20;
|
||||||
const MASTER_STATUS_REG: u16 = 0x20; // (Read only).
|
|
||||||
|
/// The port address for reading one of the status register of the master PIC. This can be either
|
||||||
|
/// the In-Service Register (ISR) or the Interrupt Request Register (IRR). This is a read only
|
||||||
|
/// operation.
|
||||||
|
const MASTER_STATUS_REG: u16 = 0x20;
|
||||||
|
|
||||||
|
/// The port address for reading or writing to the data register of the master PIC. This can be
|
||||||
|
/// used in conjunction with the command register to set up the PIC. This can also be used to mask
|
||||||
|
/// the interrupt lines so interrupts can be issued to the CPU.
|
||||||
const MASTER_DATA_REG: u16 = 0x21;
|
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.
|
/// The port address for issuing a command to the slave PIC. This is a write only operation.
|
||||||
// If set, then the PIC expects to receive a initialisation control word 4.
|
const SLAVE_COMMAND_REG: u16 = 0xA0;
|
||||||
|
|
||||||
|
/// The port address for reading one of the status register of the slave PIC. This can be either
|
||||||
|
/// the In-Service Register (ISR) or the Interrupt Request Register (IRR). This is a read only
|
||||||
|
/// operation.
|
||||||
|
const SLAVE_STATUS_REG: u16 = 0xA0;
|
||||||
|
|
||||||
|
/// The port address for reading or writing to the data register of the status PIC. This can be
|
||||||
|
/// used in conjunction with the command register to set up the PIC. This can also be used to mask
|
||||||
|
/// the interrupt lines so interrupts can be issued to the CPU.
|
||||||
|
const SLAVE_DATA_REG: u16 = 0xA1;
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
// Initialisation control word 1.
|
||||||
|
// ----------
|
||||||
|
|
||||||
|
/// 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;
|
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
|
/// 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.
|
/// PICs and initialisation control word 3 must be sent to the controller.
|
||||||
const ICW1_SINGLE_CASCADE_MODE: u8 = 0x02;
|
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.
|
/// If set, then the internal CALL address is 4. If not set, then is 8. Usually ignored by x86. So
|
||||||
// So default is not set, 0.
|
/// default is not set, 0.
|
||||||
const ICW1_CALL_ADDRESS_INTERVAL_4: u8 = 0x04;
|
const ICW1_CALL_ADDRESS_INTERVAL_4: u8 = 0x04;
|
||||||
|
|
||||||
// If set, then operating in level triggered mode. If not set, then operating in edge triggered
|
/// If set, then operating in level triggered mode. If not set, then operating in edge triggered
|
||||||
// mode.
|
/// mode.
|
||||||
const ICW1_LEVEL_TRIGGER_MODE: u8 = 0x08;
|
const ICW1_LEVEL_TRIGGER_MODE: u8 = 0x08;
|
||||||
|
|
||||||
// If set, then the PIC is to be initialised.
|
/// If set, then the PIC is to be initialised.
|
||||||
const ICW1_INITIALISATION: u8 = 0x10;
|
const ICW1_INITIALISATION: u8 = 0x10;
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
// Initialisation control word 2.
|
||||||
|
// ----------
|
||||||
|
|
||||||
// Initialisation control word 2. Map the base address of the interrupt vector table.
|
/// Initialisation control word 2. Map the base address of the interrupt vector table. The new port
|
||||||
// The new port map for the master PIC. IRQs 0-7 mapped to use interrupts 0x20-0x27
|
/// map for the master PIC. IRQs 0-7 mapped to use interrupts 0x20-0x27.
|
||||||
const ICW2_MASTER_REMAP_OFFSET: u8 = 0x20;
|
const ICW2_MASTER_REMAP_OFFSET: u8 = 0x20;
|
||||||
|
|
||||||
// The new port map for the slave PIC. IRQs 8-15 mapped to use interrupts 0x28-0x36
|
/// The new port map for the slave PIC. IRQs 8-15 mapped to use interrupts 0x28-0x2F.
|
||||||
const ICW2_SLAVE_REMAP_OFFSET: u8 = 0x28;
|
const ICW2_SLAVE_REMAP_OFFSET: u8 = 0x28;
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
// Initialisation control word 3.
|
||||||
|
// ----------
|
||||||
|
|
||||||
// Initialisation control word 3. For Telling the master and slave where the cascading
|
/// Initialisation control word 3. For Telling the master and slave where the cascading. interrupts
|
||||||
// interrupts are coming from.
|
/// are coming from. Tell the slave PIT to send interrupts to the master PIC on IRQ2.
|
||||||
// Tell the slave PIT to send interrupts to the master PIC on IRQ2
|
|
||||||
const ICW3_SLAVE_IRQ_MAP_TO_MASTER: u8 = 0x02;
|
const ICW3_SLAVE_IRQ_MAP_TO_MASTER: u8 = 0x02;
|
||||||
|
|
||||||
// Tell the master PIT to receive interrupts from the slave PIC on IRQ2
|
/// Tell the master PIT to receive interrupts from the slave PIC on IRQ2.
|
||||||
const ICW3_MASTER_IRQ_MAP_FROM_SLAVE: u8 = 0x04;
|
const ICW3_MASTER_IRQ_MAP_FROM_SLAVE: u8 = 0x04;
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
// Initialisation control word 4.
|
||||||
|
// ----------
|
||||||
|
|
||||||
// Initialisation control word 4. Tell the master and slave what mode to operate in.
|
/// Initialisation control word 4. Tell the master and slave what mode to operate in. If set, then
|
||||||
// If set, then in 80x86 mode. If not set, then in MCS-80/86 mode
|
/// in 80x86 mode. If not set, then in MCS-80/86 mode.
|
||||||
const ICW4_80x86_MODE: u8 = 0x01;
|
const ICW4_80x86_MODE: u8 = 0x01;
|
||||||
|
|
||||||
// If set, then on last interrupt acknowledge pulse the PIC automatically performs end of
|
/// If set, then on last interrupt acknowledge pulse the PIC automatically performs end of
|
||||||
// interrupt operation.
|
/// interrupt operation.
|
||||||
const ICW4_AUTO_END_OF_INTERRUPT: u8 = 0x02;
|
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
|
/// Only use if ICW4_BUFFER_MODE is set. If set, then selects master's buffer. If not set then uses
|
||||||
// slave's buffer.
|
/// slave's buffer.
|
||||||
const ICW4_BUFFER_SELECT: u8 = 0x04;
|
const ICW4_BUFFER_SELECT: u8 = 0x04;
|
||||||
|
|
||||||
// If set, then PIC operates in buffered mode.
|
/// If set, then PIC operates in buffered mode.
|
||||||
const ICW4_BUFFER_MODE: u8 = 0x08;
|
const ICW4_BUFFER_MODE: u8 = 0x08;
|
||||||
|
|
||||||
// If set, then the the system had many cascaded PIC's. Not supported in x86.
|
/// If set, then the the system had many cascaded PICs. Not supported in x86.
|
||||||
const ICW4_FULLY_NESTED_MODE: u8 = 0x10;
|
const ICW4_FULLY_NESTED_MODE: u8 = 0x10;
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
// Operation control word 1.
|
||||||
|
// ----------
|
||||||
|
|
||||||
// Operation control word 1. Interrupt masks.
|
/// Operation control word 1. Interrupt masks for IRQ0 and IRQ8.
|
||||||
const OCW1_MASK_IRQ0: u8 = 0x01;
|
const OCW1_MASK_IRQ0_8: 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.
|
/// Operation control word 1. Interrupt masks for IRQ1 and IRQ9.
|
||||||
// Interrupt level 1 upon which the controller must react. Interrupt level for the current interrupt
|
const OCW1_MASK_IRQ1_9: u8 = 0x02;
|
||||||
|
|
||||||
|
/// Operation control word 1. Interrupt masks for IRQ2 and IRQ10.
|
||||||
|
const OCW1_MASK_IRQ2_10: u8 = 0x04;
|
||||||
|
|
||||||
|
/// Operation control word 1. Interrupt masks for IRQ3 and IRQ11.
|
||||||
|
const OCW1_MASK_IRQ3_11: u8 = 0x08;
|
||||||
|
|
||||||
|
/// Operation control word 1. Interrupt masks for IRQ4 and IRQ12.
|
||||||
|
const OCW1_MASK_IRQ4_12: u8 = 0x10;
|
||||||
|
|
||||||
|
/// Operation control word 1. Interrupt masks for IRQ5 and IRQ13.
|
||||||
|
const OCW1_MASK_IRQ5_13: u8 = 0x20;
|
||||||
|
|
||||||
|
/// Operation control word 1. Interrupt masks for IRQ6 and IRQ14.
|
||||||
|
const OCW1_MASK_IRQ6_14: u8 = 0x40;
|
||||||
|
|
||||||
|
/// Operation control word 1. Interrupt masks for IRQ7 and IRQ15.
|
||||||
|
const OCW1_MASK_IRQ7_15: u8 = 0x80;
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
// Operation control word 2.
|
||||||
|
// ----------
|
||||||
|
|
||||||
|
/// 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;
|
const OCW2_INTERRUPT_LEVEL_1: u8 = 0x01;
|
||||||
|
|
||||||
// Interrupt level 2 upon which the controller must react. Interrupt level for the current interrupt
|
/// Interrupt level 2 upon which the controller must react. Interrupt level for the current
|
||||||
|
/// interrupt
|
||||||
const OCW2_INTERRUPT_LEVEL_2: u8 = 0x02;
|
const OCW2_INTERRUPT_LEVEL_2: u8 = 0x02;
|
||||||
|
|
||||||
// Interrupt level 3 upon which the controller must react. Interrupt level for the current interrupt
|
/// Interrupt level 3 upon which the controller must react. Interrupt level for the current
|
||||||
|
/// interrupt
|
||||||
const OCW2_INTERRUPT_LEVEL_3: u8 = 0x04;
|
const OCW2_INTERRUPT_LEVEL_3: u8 = 0x04;
|
||||||
|
|
||||||
// The end of interrupt command code.
|
/// The end of interrupt command code.
|
||||||
const OCW2_END_OF_INTERRUPT: u8 = 0x20;
|
const OCW2_END_OF_INTERRUPT: u8 = 0x20;
|
||||||
|
|
||||||
// Select command.
|
/// Select command.
|
||||||
const OCW2_SELECTION: u8 = 0x40;
|
const OCW2_SELECTION: u8 = 0x40;
|
||||||
|
|
||||||
// Rotation command.
|
/// Rotation command.
|
||||||
const OCW2_ROTATION: u8 = 0x80;
|
const OCW2_ROTATION: u8 = 0x80;
|
||||||
|
|
||||||
|
// ----------
|
||||||
// Operation control word 3.
|
// Operation control word 3.
|
||||||
// Read the Interrupt Request Register register
|
// ----------
|
||||||
|
|
||||||
|
/// Operation control word 3.
|
||||||
|
/// Read the Interrupt Request Register register
|
||||||
const OCW3_READ_IRR: u8 = 0x00;
|
const OCW3_READ_IRR: u8 = 0x00;
|
||||||
|
|
||||||
// Read the In Service Register register.
|
/// Read the In Service Register register.
|
||||||
const OCW3_READ_ISR: u8 = 0x01;
|
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.
|
/// 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;
|
const OCW3_ACT_ON_READ: u8 = 0x02;
|
||||||
|
|
||||||
// If set, then poll command issued. If not set, then no pool command issued.
|
/// If set, then poll command issued. If not set, then no pool command issued.
|
||||||
const OCW3_POLL_COMMAND_ISSUED: u8 = 0x04;
|
const OCW3_POLL_COMMAND_ISSUED: u8 = 0x04;
|
||||||
|
|
||||||
// This must be set for all OCW 3.
|
/// This must be set for all OCW 3.
|
||||||
const OCW3_DEFAULT: u8 = 0x08;
|
const OCW3_DEFAULT: u8 = 0x08;
|
||||||
|
|
||||||
// If set, then the special mask is set. If not set, then resets special mask.
|
// Next bit must be zero.
|
||||||
|
|
||||||
|
/// If set, then the special mask is set. If not set, then resets special mask.
|
||||||
const OCW3_SPECIAL_MASK: u8 = 0x20;
|
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
|
/// If set, then bit 5 will be acted on, so setting the special mask. If not set, then no action it
|
||||||
// taken.
|
/// taken.
|
||||||
const OCW3_ACK_ON_SPECIAL_MASK: u8 = 0x40;
|
const OCW3_ACK_ON_SPECIAL_MASK: u8 = 0x40;
|
||||||
|
|
||||||
|
// Last bit must be zero.
|
||||||
|
|
||||||
// IRQ's numbers for the PIC.
|
// ----------
|
||||||
|
// The IRQs
|
||||||
|
// ----------
|
||||||
|
|
||||||
|
/// The IRQ for the PIT.
|
||||||
pub const IRQ_PIT: u8 = 0x00;
|
pub const IRQ_PIT: u8 = 0x00;
|
||||||
|
|
||||||
|
/// The IRQ for the keyboard.
|
||||||
pub const IRQ_KEYBOARD: u8 = 0x01;
|
pub const IRQ_KEYBOARD: u8 = 0x01;
|
||||||
|
|
||||||
|
/// The IRQ for the cascade from master to slave.
|
||||||
pub const IRQ_CASCADE_FOR_SLAVE: u8 = 0x02;
|
pub const IRQ_CASCADE_FOR_SLAVE: u8 = 0x02;
|
||||||
|
|
||||||
|
/// The IRQ for the serial COM2/4.
|
||||||
pub const IRQ_SERIAL_PORT_2: u8 = 0x03;
|
pub const IRQ_SERIAL_PORT_2: u8 = 0x03;
|
||||||
|
|
||||||
|
/// The IRQ for the serial COM1/3.
|
||||||
pub const IRQ_SERIAL_PORT_1: u8 = 0x04;
|
pub const IRQ_SERIAL_PORT_1: u8 = 0x04;
|
||||||
|
|
||||||
|
/// The IRQ for the parallel port 2.
|
||||||
pub const IRQ_PARALLEL_PORT_2: u8 = 0x05;
|
pub const IRQ_PARALLEL_PORT_2: u8 = 0x05;
|
||||||
|
|
||||||
|
/// The IRQ for the floppy disk.
|
||||||
pub const IRQ_DISKETTE_DRIVE: u8 = 0x06;
|
pub const IRQ_DISKETTE_DRIVE: u8 = 0x06;
|
||||||
|
|
||||||
|
/// The IRQ for the parallel port 1.
|
||||||
pub const IRQ_PARALLEL_PORT_1: u8 = 0x07;
|
pub const IRQ_PARALLEL_PORT_1: u8 = 0x07;
|
||||||
|
|
||||||
|
/// The IRQ for the CMOS real time clock (RTC).
|
||||||
pub const IRQ_REAL_TIME_CLOCK: u8 = 0x08;
|
pub const IRQ_REAL_TIME_CLOCK: u8 = 0x08;
|
||||||
|
|
||||||
|
/// The IRQ for the CGA vertical retrace.
|
||||||
pub const IRQ_CGA_VERTICAL_RETRACE: u8 = 0x09;
|
pub const IRQ_CGA_VERTICAL_RETRACE: u8 = 0x09;
|
||||||
|
|
||||||
pub const IRQ_AUXILIARY_DEVICE: u8 = 0x0C;
|
/// Reserved.
|
||||||
|
pub const IRQ_RESERVED1: u8 = 0x0A;
|
||||||
|
|
||||||
|
/// Reserved.
|
||||||
|
pub const IRQ_RESERVED2: u8 = 0x0B;
|
||||||
|
|
||||||
|
// The IRQ for the PS/2 mouse.
|
||||||
|
pub const IRQ_PS2_MOUSE: u8 = 0x0C;
|
||||||
|
|
||||||
|
/// The IRQ for the floating point unit/co-processor.
|
||||||
pub const IRQ_FLOATING_POINT_UNIT: u8 = 0x0D;
|
pub const IRQ_FLOATING_POINT_UNIT: u8 = 0x0D;
|
||||||
pub const IRQ_HARD_DISK_CONTROLLER: u8 = 0x0E;
|
|
||||||
|
|
||||||
|
/// The IRQ for the primary hard drive controller.
|
||||||
|
pub const IRQ_PRIMARY_HARD_DISK_CONTROLLER: u8 = 0x0E;
|
||||||
|
|
||||||
// Keep track of the number of spurious IRQ's
|
/// The IRQ for the secondary hard drive controller.
|
||||||
|
pub const IRQ_SECONDARY_HARD_DISK_CONTROLLER: u8 = 0x0F;
|
||||||
|
|
||||||
|
/// Keep track of the number of spurious IRQs.
|
||||||
var spurious_irq_counter: u32 = 0;
|
var spurious_irq_counter: u32 = 0;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Send a command to the master PIC. This will send it to the master command port.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN cmd: u8 - The command to send.
|
||||||
|
///
|
||||||
inline fn sendCommandMaster(cmd: u8) void {
|
inline fn sendCommandMaster(cmd: u8) void {
|
||||||
arch.outb(MASTER_COMMAND_REG, cmd);
|
arch.outb(MASTER_COMMAND_REG, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Send a command to the salve PIC. This will send it to the salve command port.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN cmd: u8 - The command to send.
|
||||||
|
///
|
||||||
inline fn sendCommandSlave(cmd: u8) void {
|
inline fn sendCommandSlave(cmd: u8) void {
|
||||||
arch.outb(SLAVE_COMMAND_REG, cmd);
|
arch.outb(SLAVE_COMMAND_REG, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn sendDataMaster(cmd: u8) void {
|
///
|
||||||
arch.outb(MASTER_DATA_REG, cmd);
|
/// Send data to the master PIC. This will send it to the master data port.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN data: u8 - The data to send.
|
||||||
|
///
|
||||||
|
inline fn sendDataMaster(data: u8) void {
|
||||||
|
arch.outb(MASTER_DATA_REG, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn sendDataSlave(cmd: u8) void {
|
///
|
||||||
arch.outb(SLAVE_DATA_REG, cmd);
|
/// Send data to the salve PIC. This will send it to the salve data port.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN data: u8 - The data to send.
|
||||||
|
///
|
||||||
|
inline fn sendDataSlave(data: u8) void {
|
||||||
|
arch.outb(SLAVE_DATA_REG, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Read the data from the master data register. This will read from the master data port.
|
||||||
|
///
|
||||||
|
/// Return: u8
|
||||||
|
/// The data that is stored in the master data register.
|
||||||
|
///
|
||||||
inline fn readDataMaster() u8 {
|
inline fn readDataMaster() u8 {
|
||||||
return arch.inb(MASTER_DATA_REG);
|
return arch.inb(MASTER_DATA_REG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Read the data from the salve data register. This will read from the salve data port.
|
||||||
|
///
|
||||||
|
/// Return: u8
|
||||||
|
/// The data that is stored in the salve data register.
|
||||||
|
///
|
||||||
inline fn readDataSlave() u8 {
|
inline fn readDataSlave() u8 {
|
||||||
return arch.inb(SLAVE_DATA_REG);
|
return arch.inb(SLAVE_DATA_REG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Read the master interrupt request register (IRR).
|
||||||
|
///
|
||||||
|
/// Return: u8
|
||||||
|
/// The data that is stored in the master IRR.
|
||||||
|
///
|
||||||
inline fn readMasterIrr() u8 {
|
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);
|
sendCommandMaster(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_IRR);
|
||||||
return arch.inb(MASTER_STATUS_REG);
|
return arch.inb(MASTER_STATUS_REG);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn readMasterIsr() u8 {
|
///
|
||||||
sendCommandSlave(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_ISR);
|
/// Read the slave interrupt request register (IRR).
|
||||||
|
///
|
||||||
|
/// Return: u8
|
||||||
|
/// The data that is stored in the slave IRR.
|
||||||
|
///
|
||||||
|
inline fn readSlaveIrr() u8 {
|
||||||
|
sendCommandSlave(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_IRR);
|
||||||
return arch.inb(SLAVE_STATUS_REG);
|
return arch.inb(SLAVE_STATUS_REG);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn readSlaveIsr() u8 {
|
///
|
||||||
|
/// Read the master in-service register (ISR).
|
||||||
|
///
|
||||||
|
/// Return: u8
|
||||||
|
/// The data that is stored in the master ISR.
|
||||||
|
///
|
||||||
|
inline fn readMasterIsr() u8 {
|
||||||
sendCommandMaster(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_ISR);
|
sendCommandMaster(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_ISR);
|
||||||
return arch.inb(MASTER_STATUS_REG);
|
return arch.inb(MASTER_STATUS_REG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Read the slave in-service register (ISR).
|
||||||
|
///
|
||||||
|
/// Return: u8
|
||||||
|
/// The data that is stored in the slave ISR.
|
||||||
|
///
|
||||||
|
inline fn readSlaveIsr() u8 {
|
||||||
|
sendCommandSlave(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_ISR);
|
||||||
|
return arch.inb(SLAVE_STATUS_REG);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Send the end of interrupt (EOI) signal to the PIC. If the IRQ was from the master, then will
|
||||||
|
/// send the EOI to the master only. If the IRQ came from the slave, then will send the EOI to both
|
||||||
|
/// the slave and master.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN irq_num: u8 - The IRQ number to sent the EOI to.
|
||||||
|
///
|
||||||
pub fn sendEndOfInterrupt(irq_num: u8) void {
|
pub fn sendEndOfInterrupt(irq_num: u8) void {
|
||||||
if (irq_num >= 8) {
|
if (irq_num >= 8) {
|
||||||
sendCommandSlave(OCW2_END_OF_INTERRUPT);
|
sendCommandSlave(OCW2_END_OF_INTERRUPT);
|
||||||
|
@ -195,11 +365,22 @@ pub fn sendEndOfInterrupt(irq_num: u8) void {
|
||||||
sendCommandMaster(OCW2_END_OF_INTERRUPT);
|
sendCommandMaster(OCW2_END_OF_INTERRUPT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Check if the interrupt was a fake interrupt. (In short, this stops a race condition between the
|
||||||
|
/// CPU and PIC. See https://wiki.osdev.org/PIC#Spurious_IRQs for more details). If this returns
|
||||||
|
/// true, then the IRQ handler must not send a EOI back.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN irq_num: u8 - The IRQ number to check.
|
||||||
|
///
|
||||||
|
/// Return: bool
|
||||||
|
/// Whether the IRQ provided was spurious.
|
||||||
|
///
|
||||||
pub fn spuriousIrq(irq_num: u8) bool {
|
pub fn spuriousIrq(irq_num: u8) bool {
|
||||||
// Only for IRQ 7 and 15
|
// Only for IRQ 7 and 15
|
||||||
if (irq_num == 7) {
|
if (irq_num == 7) {
|
||||||
// Read master ISR
|
// Read master ISR
|
||||||
// Check the MSB is zero, if so, then is a spurious irq
|
// 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
|
// This is (1 << irq_num) or (1 << 7) to check if it is set for this IRQ
|
||||||
if ((readMasterIsr() & 0x80) == 0) {
|
if ((readMasterIsr() & 0x80) == 0) {
|
||||||
spurious_irq_counter += 1;
|
spurious_irq_counter += 1;
|
||||||
|
@ -219,21 +400,40 @@ pub fn spuriousIrq(irq_num: u8) bool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setMask(irq_num: u16) void {
|
///
|
||||||
|
/// Set the mask bit for the provided IRQ. This will prevent interrupts from triggering for this
|
||||||
|
/// IRQ.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN irq_num: u8 - The IRQ number to mask.
|
||||||
|
///
|
||||||
|
pub fn setMask(irq_num: u8) void {
|
||||||
const port: u16 = if (irq_num < 8) MASTER_DATA_REG else SLAVE_DATA_REG;
|
const port: u16 = if (irq_num < 8) MASTER_DATA_REG else SLAVE_DATA_REG;
|
||||||
const shift = @intCast(u3, irq_num % 8);
|
const shift = @intCast(u3, irq_num % 8);
|
||||||
const value: u8 = arch.inb(port) | (u8(1) << shift);
|
const value: u8 = arch.inb(port) | (u8(1) << shift);
|
||||||
arch.outb(port, value);
|
arch.outb(port, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clearMask(irq_num: u16) void {
|
///
|
||||||
|
/// Clear the mask bit for the provided IRQ. This will allow interrupts to triggering for this IRQ.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN irq_num: u8 - The IRQ number unmask.
|
||||||
|
///
|
||||||
|
pub fn clearMask(irq_num: u8) void {
|
||||||
const port: u16 = if (irq_num < 8) MASTER_DATA_REG else SLAVE_DATA_REG;
|
const port: u16 = if (irq_num < 8) MASTER_DATA_REG else SLAVE_DATA_REG;
|
||||||
const shift = @intCast(u3, irq_num % 8);
|
const shift = @intCast(u3, irq_num % 8);
|
||||||
const value: u8 = arch.inb(port) & ~(u8(1) << shift);
|
const value: u8 = arch.inb(port) & ~(u8(1) << shift);
|
||||||
arch.outb(port, value);
|
arch.outb(port, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remapIrq() void {
|
///
|
||||||
|
/// Remap the PIC interrupt lines as initially they conflict with CPU exceptions which are reserved
|
||||||
|
/// by Intel up to 0x1F. So this will move the IRQs from 0x00-0x0F to 0x20-0x2F.
|
||||||
|
///
|
||||||
|
pub fn init() void {
|
||||||
|
log.logInfo("Init pic\n");
|
||||||
|
|
||||||
// Initiate
|
// Initiate
|
||||||
sendCommandMaster(ICW1_INITIALISATION | ICW1_EXPECT_ICW4);
|
sendCommandMaster(ICW1_INITIALISATION | ICW1_EXPECT_ICW4);
|
||||||
sendCommandSlave(ICW1_INITIALISATION | ICW1_EXPECT_ICW4);
|
sendCommandSlave(ICW1_INITIALISATION | ICW1_EXPECT_ICW4);
|
||||||
|
@ -250,7 +450,365 @@ pub fn remapIrq() void {
|
||||||
sendDataMaster(ICW4_80x86_MODE);
|
sendDataMaster(ICW4_80x86_MODE);
|
||||||
sendDataSlave(ICW4_80x86_MODE);
|
sendDataSlave(ICW4_80x86_MODE);
|
||||||
|
|
||||||
// Mask
|
// Mask all interrupts
|
||||||
arch.outb(0x21, 0xFF);
|
sendDataMaster(u8(0xFF));
|
||||||
arch.outb(0xA1, 0xFF);
|
sendDataSlave(u8(0xFF));
|
||||||
|
|
||||||
|
log.logInfo("Done\n");
|
||||||
|
|
||||||
|
if (build_options.rt_test) runtimeTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
test "sendCommandMaster" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
const cmd = u8(10);
|
||||||
|
|
||||||
|
arch.addTestParams("outb", MASTER_COMMAND_REG, cmd);
|
||||||
|
|
||||||
|
sendCommandMaster(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "sendCommandSlave" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
const cmd = u8(10);
|
||||||
|
|
||||||
|
arch.addTestParams("outb", SLAVE_COMMAND_REG, cmd);
|
||||||
|
|
||||||
|
sendCommandSlave(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "sendDataMaster" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
const data = u8(10);
|
||||||
|
|
||||||
|
arch.addTestParams("outb", MASTER_DATA_REG, data);
|
||||||
|
|
||||||
|
sendDataMaster(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "sendDataSlave" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
const data = u8(10);
|
||||||
|
|
||||||
|
arch.addTestParams("outb", SLAVE_DATA_REG, data);
|
||||||
|
|
||||||
|
sendDataSlave(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "readDataMaster" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
arch.addTestParams("inb", MASTER_DATA_REG, u8(10));
|
||||||
|
|
||||||
|
expectEqual(u8(10), readDataMaster());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "readDataSlave" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
arch.addTestParams("inb", SLAVE_DATA_REG, u8(10));
|
||||||
|
|
||||||
|
expectEqual(u8(10), readDataSlave());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "readMasterIrr" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
arch.addTestParams("outb", MASTER_COMMAND_REG, u8(0x0A));
|
||||||
|
arch.addTestParams("inb", MASTER_STATUS_REG, u8(10));
|
||||||
|
|
||||||
|
expectEqual(u8(10), readMasterIrr());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "readSlaveIrr" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
arch.addTestParams("outb", SLAVE_COMMAND_REG, u8(0x0A));
|
||||||
|
arch.addTestParams("inb", SLAVE_STATUS_REG, u8(10));
|
||||||
|
|
||||||
|
expectEqual(u8(10), readSlaveIrr());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "readMasterIsr" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
arch.addTestParams("outb", MASTER_COMMAND_REG, u8(0x0B));
|
||||||
|
arch.addTestParams("inb", MASTER_STATUS_REG, u8(10));
|
||||||
|
|
||||||
|
expectEqual(u8(10), readMasterIsr());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "readSlaveIsr" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
arch.addTestParams("outb", SLAVE_COMMAND_REG, u8(0x0B));
|
||||||
|
arch.addTestParams("inb", SLAVE_STATUS_REG, u8(10));
|
||||||
|
|
||||||
|
expectEqual(u8(10), readSlaveIsr());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "sendEndOfInterrupt master only" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
var i = u8(0);
|
||||||
|
while (i < 8) : (i += 1) {
|
||||||
|
arch.addTestParams("outb", MASTER_COMMAND_REG, OCW2_END_OF_INTERRUPT);
|
||||||
|
|
||||||
|
sendEndOfInterrupt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "sendEndOfInterrupt master and slave" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
var i = u8(8);
|
||||||
|
while (i < 16) : (i += 1) {
|
||||||
|
arch.addTestParams("outb", SLAVE_COMMAND_REG, OCW2_END_OF_INTERRUPT);
|
||||||
|
arch.addTestParams("outb", MASTER_COMMAND_REG, OCW2_END_OF_INTERRUPT);
|
||||||
|
|
||||||
|
sendEndOfInterrupt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "spuriousIrq not spurious IRQ number" {
|
||||||
|
// Pre testing
|
||||||
|
expectEqual(u32(0), spurious_irq_counter);
|
||||||
|
|
||||||
|
var i = u8(0);
|
||||||
|
while (i < 16) : (i += 1) {
|
||||||
|
if (i != 7 and i != 15) {
|
||||||
|
expectEqual(false, spuriousIrq(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post testing
|
||||||
|
expectEqual(u32(0), spurious_irq_counter);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
spurious_irq_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "spuriousIrq spurious master IRQ number not spurious" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
arch.addTestParams("outb", MASTER_COMMAND_REG, u8(0x0B));
|
||||||
|
// Return 0x80 from readMasterIsr() which will mean this was a real IRQ
|
||||||
|
arch.addTestParams("inb", MASTER_STATUS_REG, u8(0x80));
|
||||||
|
|
||||||
|
// Pre testing
|
||||||
|
expectEqual(u32(0), spurious_irq_counter);
|
||||||
|
|
||||||
|
// Call function
|
||||||
|
expectEqual(false, spuriousIrq(u8(7)));
|
||||||
|
|
||||||
|
// Post testing
|
||||||
|
expectEqual(u32(0), spurious_irq_counter);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
spurious_irq_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "spuriousIrq spurious master IRQ number spurious" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
arch.addTestParams("outb", MASTER_COMMAND_REG, u8(0x0B));
|
||||||
|
// Return 0x0 from readMasterIsr() which will mean this was a spurious IRQ
|
||||||
|
arch.addTestParams("inb", MASTER_STATUS_REG, u8(0x0));
|
||||||
|
|
||||||
|
// Pre testing
|
||||||
|
expectEqual(u32(0), spurious_irq_counter);
|
||||||
|
|
||||||
|
// Call function
|
||||||
|
expectEqual(true, spuriousIrq(u8(7)));
|
||||||
|
|
||||||
|
// Post testing
|
||||||
|
expectEqual(u32(1), spurious_irq_counter);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
spurious_irq_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "spuriousIrq spurious slave IRQ number not spurious" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
arch.addTestParams("outb", SLAVE_COMMAND_REG, u8(0x0B));
|
||||||
|
// Return 0x80 from readSlaveIsr() which will mean this was a real IRQ
|
||||||
|
arch.addTestParams("inb", SLAVE_STATUS_REG, u8(0x80));
|
||||||
|
|
||||||
|
// Pre testing
|
||||||
|
expectEqual(u32(0), spurious_irq_counter);
|
||||||
|
|
||||||
|
// Call function
|
||||||
|
expectEqual(false, spuriousIrq(u8(15)));
|
||||||
|
|
||||||
|
// Post testing
|
||||||
|
expectEqual(u32(0), spurious_irq_counter);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
spurious_irq_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "spuriousIrq spurious slave IRQ number spurious" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
arch.addTestParams("outb", SLAVE_COMMAND_REG, u8(0x0B));
|
||||||
|
// Return 0x0 from readSlaveIsr() which will mean this was a spurious IRQ
|
||||||
|
arch.addTestParams("inb", SLAVE_STATUS_REG, u8(0x0));
|
||||||
|
// A EOI will be sent for a spurious IRQ 15
|
||||||
|
arch.addTestParams("outb", MASTER_COMMAND_REG, OCW2_END_OF_INTERRUPT);
|
||||||
|
|
||||||
|
// Pre testing
|
||||||
|
expectEqual(u32(0), spurious_irq_counter);
|
||||||
|
|
||||||
|
// Call function
|
||||||
|
expectEqual(true, spuriousIrq(u8(15)));
|
||||||
|
|
||||||
|
// Post testing
|
||||||
|
expectEqual(u32(1), spurious_irq_counter);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
spurious_irq_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "setMask master IRQ masked" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
// Going to assume all bits are masked out
|
||||||
|
arch.addTestParams("inb", MASTER_DATA_REG, u8(0xFF));
|
||||||
|
// Expect the 2nd bit to be set
|
||||||
|
arch.addTestParams("outb", MASTER_DATA_REG, u8(0xFF));
|
||||||
|
|
||||||
|
setMask(u8(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "setMask master IRQ unmasked" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
// IRQ already unmasked
|
||||||
|
arch.addTestParams("inb", MASTER_DATA_REG, u8(0xFD));
|
||||||
|
// Expect the 2nd bit to be set
|
||||||
|
arch.addTestParams("outb", MASTER_DATA_REG, u8(0xFF));
|
||||||
|
|
||||||
|
setMask(u8(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "clearMask master IRQ masked" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
// Going to assume all bits are masked out
|
||||||
|
arch.addTestParams("inb", MASTER_DATA_REG, u8(0xFF));
|
||||||
|
// Expect the 2nd bit to be clear
|
||||||
|
arch.addTestParams("outb", MASTER_DATA_REG, u8(0xFD));
|
||||||
|
|
||||||
|
clearMask(u8(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "clearMask master IRQ unmasked" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
// IRQ already unmasked
|
||||||
|
arch.addTestParams("inb", MASTER_DATA_REG, u8(0xFD));
|
||||||
|
// Expect the 2nd bit to still be clear
|
||||||
|
arch.addTestParams("outb", MASTER_DATA_REG, u8(0xFD));
|
||||||
|
|
||||||
|
clearMask(u8(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "init" {
|
||||||
|
// Set up
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
// Just a long list of OUT instructions setting up the PIC
|
||||||
|
arch.addTestParams(
|
||||||
|
"outb",
|
||||||
|
MASTER_COMMAND_REG,
|
||||||
|
ICW1_INITIALISATION | ICW1_EXPECT_ICW4,
|
||||||
|
SLAVE_COMMAND_REG,
|
||||||
|
ICW1_INITIALISATION | ICW1_EXPECT_ICW4,
|
||||||
|
MASTER_DATA_REG,
|
||||||
|
ICW2_MASTER_REMAP_OFFSET,
|
||||||
|
SLAVE_DATA_REG,
|
||||||
|
ICW2_SLAVE_REMAP_OFFSET,
|
||||||
|
MASTER_DATA_REG,
|
||||||
|
ICW3_MASTER_IRQ_MAP_FROM_SLAVE,
|
||||||
|
SLAVE_DATA_REG,
|
||||||
|
ICW3_SLAVE_IRQ_MAP_TO_MASTER,
|
||||||
|
MASTER_DATA_REG,
|
||||||
|
ICW4_80x86_MODE,
|
||||||
|
SLAVE_DATA_REG,
|
||||||
|
ICW4_80x86_MODE,
|
||||||
|
MASTER_DATA_REG,
|
||||||
|
u8(0xFF),
|
||||||
|
SLAVE_DATA_REG,
|
||||||
|
u8(0xFF),
|
||||||
|
);
|
||||||
|
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Test that all the PIC masks are set so no interrupts can fire.
|
||||||
|
///
|
||||||
|
fn rt_picAllMasked() void {
|
||||||
|
if (readDataMaster() != 0xFF) {
|
||||||
|
panic(@errorReturnTrace(), "Master masks are not set, found: {}\n", readDataMaster());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readDataSlave() != 0xFF) {
|
||||||
|
panic(@errorReturnTrace(), "Slave masks are not set, found: {}\n", readDataSlave());
|
||||||
|
}
|
||||||
|
|
||||||
|
log.logInfo("PIC: Tested masking\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Run all the runtime tests.
|
||||||
|
///
|
||||||
|
fn runtimeTests() void {
|
||||||
|
rt_picAllMasked();
|
||||||
}
|
}
|
||||||
|
|
|
@ -633,100 +633,6 @@ pub fn init() void {
|
||||||
if (build_options.rt_test) runtimeTests();
|
if (build_options.rt_test) runtimeTests();
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Test the init function set up everything properly.
|
|
||||||
///
|
|
||||||
fn rt_initialisedGlobals() void {
|
|
||||||
if (@ptrToInt(video_buffer.ptr) != @ptrToInt(&KERNEL_ADDR_OFFSET) + 0xB8000) {
|
|
||||||
panic(@errorReturnTrace(), "Video buffer not at correct virtual address, found: {}\n", @ptrToInt(video_buffer.ptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page_index != 0) {
|
|
||||||
panic(@errorReturnTrace(), "Page index not at zero, found: {}\n", page_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (colour != vga.entryColour(vga.COLOUR_LIGHT_GREY, vga.COLOUR_BLACK)) {
|
|
||||||
panic(@errorReturnTrace(), "Colour not set up properly, found: {}\n", colour);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blank != vga.entry(0, colour)) {
|
|
||||||
panic(@errorReturnTrace(), "Blank not set up properly, found: {}\n", blank);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the screen isn't all blank
|
|
||||||
var all_blank = true;
|
|
||||||
for (video_buffer) |buf| {
|
|
||||||
if (buf != blank and buf != 0) {
|
|
||||||
all_blank = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (all_blank) {
|
|
||||||
panic(@errorReturnTrace(), "Screen all blank, should have logo and page number\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
log.logInfo("TTY: Tested globals\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Test printing a string will output to the screen. This will check both the video memory and
|
|
||||||
/// the pages.
|
|
||||||
///
|
|
||||||
fn rt_printString() void {
|
|
||||||
const text = "abcdefg";
|
|
||||||
const clear_text = "\x08" ** text.len;
|
|
||||||
|
|
||||||
print(text);
|
|
||||||
|
|
||||||
// Check the video memory
|
|
||||||
var counter = u32(0);
|
|
||||||
for (video_buffer) |buf| {
|
|
||||||
if (counter < text.len and buf == vga.entry(text[counter], colour)) {
|
|
||||||
counter += 1;
|
|
||||||
} else if (counter == text.len) {
|
|
||||||
// Found all the text
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
counter = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (counter != text.len) {
|
|
||||||
panic(@errorReturnTrace(), "Didn't find the printed text in video memory\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the pages
|
|
||||||
counter = 0;
|
|
||||||
for (pages[0]) |c| {
|
|
||||||
if (counter < text.len and c == vga.entry(text[counter], colour)) {
|
|
||||||
counter += 1;
|
|
||||||
} else if (counter == text.len) {
|
|
||||||
// Found all the text
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
counter = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (counter != text.len) {
|
|
||||||
panic(@errorReturnTrace(), "Didn't find the printed text in pages\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the text
|
|
||||||
print(clear_text);
|
|
||||||
|
|
||||||
log.logInfo("TTY: Tested printing\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Run all the runtime tests.
|
|
||||||
///
|
|
||||||
fn runtimeTests() void {
|
|
||||||
rt_initialisedGlobals();
|
|
||||||
rt_printString();
|
|
||||||
}
|
|
||||||
|
|
||||||
const test_colour: u8 = vga.orig_entryColour(vga.COLOUR_LIGHT_GREY, vga.COLOUR_BLACK);
|
const test_colour: u8 = vga.orig_entryColour(vga.COLOUR_LIGHT_GREY, vga.COLOUR_BLACK);
|
||||||
var test_video_buffer: [VIDEO_BUFFER_SIZE]u16 = [_]u16{0} ** VIDEO_BUFFER_SIZE;
|
var test_video_buffer: [VIDEO_BUFFER_SIZE]u16 = [_]u16{0} ** VIDEO_BUFFER_SIZE;
|
||||||
|
|
||||||
|
@ -2196,3 +2102,97 @@ test "init not 0,0" {
|
||||||
// Tear down
|
// Tear down
|
||||||
resetGlobals();
|
resetGlobals();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Test the init function set up everything properly.
|
||||||
|
///
|
||||||
|
fn rt_initialisedGlobals() void {
|
||||||
|
if (@ptrToInt(video_buffer.ptr) != @ptrToInt(&KERNEL_ADDR_OFFSET) + 0xB8000) {
|
||||||
|
panic(@errorReturnTrace(), "Video buffer not at correct virtual address, found: {}\n", @ptrToInt(video_buffer.ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page_index != 0) {
|
||||||
|
panic(@errorReturnTrace(), "Page index not at zero, found: {}\n", page_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colour != vga.entryColour(vga.COLOUR_LIGHT_GREY, vga.COLOUR_BLACK)) {
|
||||||
|
panic(@errorReturnTrace(), "Colour not set up properly, found: {}\n", colour);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blank != vga.entry(0, colour)) {
|
||||||
|
panic(@errorReturnTrace(), "Blank not set up properly, found: {}\n", blank);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the screen isn't all blank
|
||||||
|
var all_blank = true;
|
||||||
|
for (video_buffer) |buf| {
|
||||||
|
if (buf != blank and buf != 0) {
|
||||||
|
all_blank = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (all_blank) {
|
||||||
|
panic(@errorReturnTrace(), "Screen all blank, should have logo and page number\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.logInfo("TTY: Tested globals\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Test printing a string will output to the screen. This will check both the video memory and
|
||||||
|
/// the pages.
|
||||||
|
///
|
||||||
|
fn rt_printString() void {
|
||||||
|
const text = "abcdefg";
|
||||||
|
const clear_text = "\x08" ** text.len;
|
||||||
|
|
||||||
|
print(text);
|
||||||
|
|
||||||
|
// Check the video memory
|
||||||
|
var counter = u32(0);
|
||||||
|
for (video_buffer) |buf| {
|
||||||
|
if (counter < text.len and buf == vga.entry(text[counter], colour)) {
|
||||||
|
counter += 1;
|
||||||
|
} else if (counter == text.len) {
|
||||||
|
// Found all the text
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
counter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter != text.len) {
|
||||||
|
panic(@errorReturnTrace(), "Didn't find the printed text in video memory\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the pages
|
||||||
|
counter = 0;
|
||||||
|
for (pages[0]) |c| {
|
||||||
|
if (counter < text.len and c == vga.entry(text[counter], colour)) {
|
||||||
|
counter += 1;
|
||||||
|
} else if (counter == text.len) {
|
||||||
|
// Found all the text
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
counter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter != text.len) {
|
||||||
|
panic(@errorReturnTrace(), "Didn't find the printed text in pages\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the text
|
||||||
|
print(clear_text);
|
||||||
|
|
||||||
|
log.logInfo("TTY: Tested printing\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Run all the runtime tests.
|
||||||
|
///
|
||||||
|
fn runtimeTests() void {
|
||||||
|
rt_initialisedGlobals();
|
||||||
|
rt_printString();
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,9 @@ def get_test_cases(TestCase):
|
||||||
TestCase("GDT tests", [r"GDT: Tested loading GDT"]),
|
TestCase("GDT tests", [r"GDT: Tested loading GDT"]),
|
||||||
TestCase("IDT init", [r"Init idt", r"Done"]),
|
TestCase("IDT init", [r"Init idt", r"Done"]),
|
||||||
TestCase("IDT tests", [r"IDT: Tested loading IDT"]),
|
TestCase("IDT tests", [r"IDT: Tested loading IDT"]),
|
||||||
TestCase("PIT init", [r"Init pit", r".+", "Done"]),
|
TestCase("PIC init", [r"Init pic", r"Done"]),
|
||||||
TestCase("Syscalls init", [r"Init syscalls", "Done"]),
|
TestCase("PIC tests", [r"PIC: Tested masking"]),
|
||||||
|
TestCase("PIT init", [r"Init pit", r".+", r"Done"]),
|
||||||
|
TestCase("Syscalls init", [r"Init syscalls", r"Done"]),
|
||||||
TestCase("Syscall tests", [r"Syscalls: Tested no args", r"Syscalls: Tested 1 arg", r"Syscalls: Tested 2 args", r"Syscalls: Tested 3 args", r"Syscalls: Tested 4 args", r"Syscalls: Tested 5 args"])
|
TestCase("Syscall tests", [r"Syscalls: Tested no args", r"Syscalls: Tested 1 arg", r"Syscalls: Tested 2 args", r"Syscalls: Tested 3 args", r"Syscalls: Tested 4 args", r"Syscalls: Tested 5 args"])
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in a new issue