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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Edward Dean
						Edward Dean