Implement ISR stubs and PIC driver for hardware interrupt handling (AI)
- Reworked IDT initialization to register all 32 CPU exception handlers (ISR 0-31) and 16 hardware interrupt handlers (IRQ 0-15, mapped to IDT entries 32-47). - Created assembly stubs in interrupts.S using macros for ISRs with and without error codes, plus IRQ stubs. All route through a common stub that saves registers, loads kernel data segment, and calls the C handler. - Added isr.c with a unified interrupt dispatcher that handles both exceptions (halts on fault) and hardware IRQs (sends EOI via PIC). - Implemented PIC (8259) driver in pic.c with full initialization sequence that remaps IRQ 0-7 to IDT 32-39 and IRQ 8-15 to IDT 40-47. Includes mask/unmask and EOI support. - Extracted port I/O primitives (inb, outb, io_wait) into port_io.h header for reuse across drivers. - Kernel now initializes PIC after IDT and enables interrupts with STI.
This commit is contained in:
@@ -5,6 +5,8 @@ add_executable(kernel
|
||||
gdt_flush.S
|
||||
gdt.c
|
||||
idt.c
|
||||
isr.c
|
||||
pic.c
|
||||
interrupts.S
|
||||
kernel.c
|
||||
)
|
||||
|
||||
158
src/idt.c
158
src/idt.c
@@ -1,46 +1,136 @@
|
||||
#include "idt.h"
|
||||
#include <stdint.h>
|
||||
#include <stddef.h> /* for memset */
|
||||
#include <string.h> // For memset
|
||||
|
||||
idt_entry_t idt_entries[256];
|
||||
idt_ptr_t idt_ptr;
|
||||
// The IDT itself
|
||||
idt_entry_t idt[256];
|
||||
idt_ptr_t idt_ptr;
|
||||
|
||||
extern void idt_load();
|
||||
|
||||
static void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags)
|
||||
{
|
||||
idt_entries[num].base_lo = base & 0xFFFF;
|
||||
idt_entries[num].base_hi = (base >> 16) & 0xFFFF;
|
||||
|
||||
idt_entries[num].sel = sel;
|
||||
idt_entries[num].always0 = 0;
|
||||
// We must uncomment the OR below when we get to using user-mode.
|
||||
// It sets the interrupt gate's privilege level to 3.
|
||||
idt_entries[num].flags = flags /* | 0x60 */;
|
||||
// Helper to set a gate in the IDT
|
||||
static void set_idt_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags) {
|
||||
idt[num].base_lo = base & 0xFFFF;
|
||||
idt[num].base_hi = (base >> 16) & 0xFFFF;
|
||||
idt[num].sel = sel;
|
||||
idt[num].always0 = 0;
|
||||
// flags: 0x8E = 10001110 (Present, Ring0, 32-bit Interrupt Gate)
|
||||
idt[num].flags = flags;
|
||||
}
|
||||
|
||||
/* Note: memset implementation because we don't link with libc */
|
||||
static void *memset(void *s, int c, size_t n)
|
||||
{
|
||||
unsigned char *p = s;
|
||||
while(n--)
|
||||
*p++ = (unsigned char)c;
|
||||
return s;
|
||||
}
|
||||
// Exception Handlers (ISRs)
|
||||
extern void isr0();
|
||||
extern void isr1();
|
||||
extern void isr2();
|
||||
extern void isr3();
|
||||
extern void isr4();
|
||||
extern void isr5();
|
||||
extern void isr6();
|
||||
extern void isr7();
|
||||
extern void isr8();
|
||||
extern void isr9();
|
||||
extern void isr10();
|
||||
extern void isr11();
|
||||
extern void isr12();
|
||||
extern void isr13();
|
||||
extern void isr14();
|
||||
extern void isr15();
|
||||
extern void isr16();
|
||||
extern void isr17();
|
||||
extern void isr18();
|
||||
extern void isr19();
|
||||
extern void isr20();
|
||||
extern void isr21();
|
||||
extern void isr22();
|
||||
extern void isr23();
|
||||
extern void isr24();
|
||||
extern void isr25();
|
||||
extern void isr26();
|
||||
extern void isr27();
|
||||
extern void isr28();
|
||||
extern void isr29();
|
||||
extern void isr30();
|
||||
extern void isr31();
|
||||
|
||||
void init_idt()
|
||||
{
|
||||
// Hardware Interrupt Handlers (IRQs)
|
||||
extern void irq0();
|
||||
extern void irq1();
|
||||
extern void irq2();
|
||||
extern void irq3();
|
||||
extern void irq4();
|
||||
extern void irq5();
|
||||
extern void irq6();
|
||||
extern void irq7();
|
||||
extern void irq8();
|
||||
extern void irq9();
|
||||
extern void irq10();
|
||||
extern void irq11();
|
||||
extern void irq12();
|
||||
extern void irq13();
|
||||
extern void irq14();
|
||||
extern void irq15();
|
||||
|
||||
void init_idt() {
|
||||
// 1. Set up the IDT pointer
|
||||
idt_ptr.limit = sizeof(idt_entry_t) * 256 - 1;
|
||||
idt_ptr.base = (uint32_t)&idt_entries;
|
||||
idt_ptr.base = (uint32_t)&idt;
|
||||
|
||||
memset(&idt_entries, 0, sizeof(idt_entry_t) * 256);
|
||||
// 2. Clear the IDT
|
||||
memset(&idt, 0, sizeof(idt_entry_t) * 256);
|
||||
|
||||
/* Determine valid flags:
|
||||
* 0x8E = 1000 1110
|
||||
* P=1, DPL=00, S=0 (System), Type=1110 (32-bit Interrupt Gate) */
|
||||
// 3. Set the ISRs (Exceptions 0-31)
|
||||
// Code Selector is 0x08 usually (defined in GDT)
|
||||
// Flags: 0x8E = Present(1), DPL(00), Storage(0), GateType(1110 = 32-bit Int)
|
||||
set_idt_gate( 0, (uint32_t)isr0, 0x08, 0x8E);
|
||||
set_idt_gate( 1, (uint32_t)isr1, 0x08, 0x8E);
|
||||
set_idt_gate( 2, (uint32_t)isr2, 0x08, 0x8E);
|
||||
set_idt_gate( 3, (uint32_t)isr3, 0x08, 0x8E);
|
||||
set_idt_gate( 4, (uint32_t)isr4, 0x08, 0x8E);
|
||||
set_idt_gate( 5, (uint32_t)isr5, 0x08, 0x8E);
|
||||
set_idt_gate( 6, (uint32_t)isr6, 0x08, 0x8E);
|
||||
set_idt_gate( 7, (uint32_t)isr7, 0x08, 0x8E);
|
||||
set_idt_gate( 8, (uint32_t)isr8, 0x08, 0x8E);
|
||||
set_idt_gate( 9, (uint32_t)isr9, 0x08, 0x8E);
|
||||
set_idt_gate(10, (uint32_t)isr10, 0x08, 0x8E);
|
||||
set_idt_gate(11, (uint32_t)isr11, 0x08, 0x8E);
|
||||
set_idt_gate(12, (uint32_t)isr12, 0x08, 0x8E);
|
||||
set_idt_gate(13, (uint32_t)isr13, 0x08, 0x8E);
|
||||
set_idt_gate(14, (uint32_t)isr14, 0x08, 0x8E);
|
||||
set_idt_gate(15, (uint32_t)isr15, 0x08, 0x8E);
|
||||
set_idt_gate(16, (uint32_t)isr16, 0x08, 0x8E);
|
||||
set_idt_gate(17, (uint32_t)isr17, 0x08, 0x8E);
|
||||
set_idt_gate(18, (uint32_t)isr18, 0x08, 0x8E);
|
||||
set_idt_gate(19, (uint32_t)isr19, 0x08, 0x8E);
|
||||
set_idt_gate(20, (uint32_t)isr20, 0x08, 0x8E);
|
||||
set_idt_gate(21, (uint32_t)isr21, 0x08, 0x8E);
|
||||
set_idt_gate(22, (uint32_t)isr22, 0x08, 0x8E);
|
||||
set_idt_gate(23, (uint32_t)isr23, 0x08, 0x8E);
|
||||
set_idt_gate(24, (uint32_t)isr24, 0x08, 0x8E);
|
||||
set_idt_gate(25, (uint32_t)isr25, 0x08, 0x8E);
|
||||
set_idt_gate(26, (uint32_t)isr26, 0x08, 0x8E);
|
||||
set_idt_gate(27, (uint32_t)isr27, 0x08, 0x8E);
|
||||
set_idt_gate(28, (uint32_t)isr28, 0x08, 0x8E);
|
||||
set_idt_gate(29, (uint32_t)isr29, 0x08, 0x8E);
|
||||
set_idt_gate(30, (uint32_t)isr30, 0x08, 0x8E);
|
||||
set_idt_gate(31, (uint32_t)isr31, 0x08, 0x8E);
|
||||
|
||||
/* For now, just load the IDT without any entries to verify loading works without crashing.
|
||||
Later we will set up ISRs. */
|
||||
// 4. Set the IRQs (Remapped to 32-47)
|
||||
set_idt_gate(32, (uint32_t)irq0, 0x08, 0x8E);
|
||||
set_idt_gate(33, (uint32_t)irq1, 0x08, 0x8E);
|
||||
set_idt_gate(34, (uint32_t)irq2, 0x08, 0x8E);
|
||||
set_idt_gate(35, (uint32_t)irq3, 0x08, 0x8E);
|
||||
set_idt_gate(36, (uint32_t)irq4, 0x08, 0x8E);
|
||||
set_idt_gate(37, (uint32_t)irq5, 0x08, 0x8E);
|
||||
set_idt_gate(38, (uint32_t)irq6, 0x08, 0x8E);
|
||||
set_idt_gate(39, (uint32_t)irq7, 0x08, 0x8E);
|
||||
set_idt_gate(40, (uint32_t)irq8, 0x08, 0x8E);
|
||||
set_idt_gate(41, (uint32_t)irq9, 0x08, 0x8E);
|
||||
set_idt_gate(42, (uint32_t)irq10, 0x08, 0x8E);
|
||||
set_idt_gate(43, (uint32_t)irq11, 0x08, 0x8E);
|
||||
set_idt_gate(44, (uint32_t)irq12, 0x08, 0x8E);
|
||||
set_idt_gate(45, (uint32_t)irq13, 0x08, 0x8E);
|
||||
set_idt_gate(46, (uint32_t)irq14, 0x08, 0x8E);
|
||||
set_idt_gate(47, (uint32_t)irq15, 0x08, 0x8E);
|
||||
|
||||
idt_load((uint32_t)&idt_ptr);
|
||||
// 5. Load the IDT using assembly instruction 'lidt'
|
||||
// We can use inline assembly or a helper function.
|
||||
// Assuming lid is available via inline asm or similar to gdt_flush
|
||||
__asm__ volatile("lidt (%0)" : : "r" (&idt_ptr));
|
||||
}
|
||||
|
||||
117
src/interrupts.S
117
src/interrupts.S
@@ -2,6 +2,119 @@
|
||||
.global idt_load
|
||||
.type idt_load, @function
|
||||
idt_load:
|
||||
mov 4(%esp), %eax /* Turn the argument into the EAX register */
|
||||
lidt (%eax) /* Load the IDT pointer */
|
||||
mov 4(%esp), %eax
|
||||
lidt (%eax)
|
||||
ret
|
||||
|
||||
/* Common ISR stub */
|
||||
isr_common_stub:
|
||||
pusha
|
||||
|
||||
/* Save segment registers */
|
||||
mov %ds, %ax
|
||||
push %eax
|
||||
|
||||
/* Load kernel data segment */
|
||||
mov $0x10, %ax
|
||||
mov %ax, %ds
|
||||
mov %ax, %es
|
||||
mov %ax, %fs
|
||||
mov %ax, %gs
|
||||
|
||||
/* Push pointer to stack structure as argument for C handler */
|
||||
push %esp
|
||||
call isr_handler
|
||||
add $4, %esp /* Clean up pushed pointer */
|
||||
|
||||
/* Restore segment registers */
|
||||
pop %eax
|
||||
mov %eax, %ds
|
||||
mov %eax, %es
|
||||
mov %eax, %fs
|
||||
mov %eax, %gs
|
||||
|
||||
popa
|
||||
add $8, %esp /* Cleans up error code and ISR number */
|
||||
iret
|
||||
|
||||
/* Macro for exceptions with NO Error Code */
|
||||
.macro ISR_NOERRCODE num
|
||||
.global isr\num
|
||||
.type isr\num, @function
|
||||
isr\num:
|
||||
cli
|
||||
push $0
|
||||
push $\num
|
||||
jmp isr_common_stub
|
||||
.endm
|
||||
|
||||
/* Macro for exceptions WITH Error Code */
|
||||
.macro ISR_ERRCODE num
|
||||
.global isr\num
|
||||
.type isr\num, @function
|
||||
isr\num:
|
||||
cli
|
||||
push $\num
|
||||
jmp isr_common_stub
|
||||
.endm
|
||||
|
||||
ISR_NOERRCODE 0
|
||||
ISR_NOERRCODE 1
|
||||
ISR_NOERRCODE 2
|
||||
ISR_NOERRCODE 3
|
||||
ISR_NOERRCODE 4
|
||||
ISR_NOERRCODE 5
|
||||
ISR_NOERRCODE 6
|
||||
ISR_NOERRCODE 7
|
||||
ISR_ERRCODE 8
|
||||
ISR_NOERRCODE 9
|
||||
ISR_ERRCODE 10
|
||||
ISR_ERRCODE 11
|
||||
ISR_ERRCODE 12
|
||||
ISR_ERRCODE 13
|
||||
ISR_ERRCODE 14
|
||||
ISR_NOERRCODE 15
|
||||
ISR_NOERRCODE 16
|
||||
ISR_ERRCODE 17
|
||||
ISR_NOERRCODE 18
|
||||
ISR_NOERRCODE 19
|
||||
ISR_NOERRCODE 20
|
||||
ISR_NOERRCODE 21
|
||||
ISR_NOERRCODE 22
|
||||
ISR_NOERRCODE 23
|
||||
ISR_NOERRCODE 24
|
||||
ISR_NOERRCODE 25
|
||||
ISR_NOERRCODE 26
|
||||
ISR_NOERRCODE 27
|
||||
ISR_NOERRCODE 28
|
||||
ISR_NOERRCODE 29
|
||||
ISR_ERRCODE 30
|
||||
ISR_NOERRCODE 31
|
||||
/* Macro for IRQs (Hardware Interrupts) */
|
||||
.macro ISR_IRQ num, idt_index
|
||||
.global irq\num
|
||||
.type irq\num, @function
|
||||
irq\num:
|
||||
cli
|
||||
push $0
|
||||
push $\idt_index
|
||||
jmp isr_common_stub
|
||||
.endm
|
||||
|
||||
/* Hardware Interrupts */
|
||||
ISR_IRQ 0, 32
|
||||
ISR_IRQ 1, 33
|
||||
ISR_IRQ 2, 34
|
||||
ISR_IRQ 3, 35
|
||||
ISR_IRQ 4, 36
|
||||
ISR_IRQ 5, 37
|
||||
ISR_IRQ 6, 38
|
||||
ISR_IRQ 7, 39
|
||||
ISR_IRQ 8, 40
|
||||
ISR_IRQ 9, 41
|
||||
ISR_IRQ 10, 42
|
||||
ISR_IRQ 11, 43
|
||||
ISR_IRQ 12, 44
|
||||
ISR_IRQ 13, 45
|
||||
ISR_IRQ 14, 46
|
||||
ISR_IRQ 15, 47
|
||||
|
||||
75
src/isr.c
Normal file
75
src/isr.c
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "isr.h"
|
||||
#include "pic.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/* Forward declaration for kernel panic or similar */
|
||||
void offset_print(const char *str);
|
||||
void print_hex(uint32_t val);
|
||||
|
||||
/* Exception messages */
|
||||
char *exception_messages[] = {
|
||||
"Division By Zero",
|
||||
"Debug",
|
||||
"Non Maskable Interrupt",
|
||||
"Breakpoint",
|
||||
"Into Detected Overflow",
|
||||
"Out of Bounds",
|
||||
"Invalid Opcode",
|
||||
"No Coprocessor",
|
||||
"Double Fault",
|
||||
"Coprocessor Segment Overrun",
|
||||
"Bad TSS",
|
||||
"Segment Not Present",
|
||||
"Stack Fault",
|
||||
"General Protection Fault",
|
||||
"Page Fault",
|
||||
"Unknown Interrupt",
|
||||
"Coprocessor Fault",
|
||||
"Alignment Check",
|
||||
"Machine Check",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved"
|
||||
};
|
||||
|
||||
void isr_handler(registers_t *regs)
|
||||
{
|
||||
// If it's a hardware interrupt (IRQ), we must acknowledge it
|
||||
if (regs->int_no >= 32 && regs->int_no < 48) {
|
||||
// Send EOI to PIC (IRQ number 0-15)
|
||||
pic_send_eoi(regs->int_no - 32);
|
||||
|
||||
// Here we would call the registered handler for this IRQ
|
||||
// For now, just print something for the timer tick so we know it works,
|
||||
// but limit it to avoid flooding the log.
|
||||
if (regs->int_no == 32) {
|
||||
// Timer tick - do nothing verbose
|
||||
// offset_print(".");
|
||||
} else if (regs->int_no == 33) {
|
||||
// Keyboard
|
||||
offset_print("Keyboard IRQ!\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
offset_print("received interrupt: ");
|
||||
print_hex(regs->int_no);
|
||||
offset_print("\n");
|
||||
|
||||
if (regs->int_no < 32)
|
||||
{
|
||||
offset_print(exception_messages[regs->int_no]);
|
||||
offset_print(" Exception. System Halted!\n");
|
||||
for (;;) ;
|
||||
}
|
||||
}
|
||||
18
src/isr.h
Normal file
18
src/isr.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef ISR_H
|
||||
#define ISR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct registers
|
||||
{
|
||||
uint32_t ds; // Data segment selector
|
||||
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha
|
||||
uint32_t int_no, err_code; // Interrupt number and error code (if applicable)
|
||||
uint32_t eip, cs, eflags, useresp, ss; // Pushed by the processor automatically
|
||||
} registers_t;
|
||||
|
||||
typedef void (*isr_t)(registers_t*);
|
||||
|
||||
void isr_handler(registers_t* regs);
|
||||
|
||||
#endif
|
||||
16
src/kernel.c
16
src/kernel.c
@@ -3,11 +3,8 @@
|
||||
#include <stddef.h>
|
||||
#include "gdt.h"
|
||||
#include "idt.h"
|
||||
|
||||
static inline void outb(uint16_t port, uint8_t val)
|
||||
{
|
||||
asm volatile ( "outb %b0, %w1" : : "a"(val), "Nd"(port) );
|
||||
}
|
||||
#include "pic.h"
|
||||
#include "port_io.h"
|
||||
|
||||
void offset_print(const char *str)
|
||||
{
|
||||
@@ -46,6 +43,13 @@ void kernel_main(uint32_t magic, uint32_t addr) {
|
||||
|
||||
init_idt();
|
||||
offset_print("IDT initialized\n");
|
||||
|
||||
|
||||
init_pic();
|
||||
offset_print("PIC initialized\n");
|
||||
|
||||
/* Enable interrupts */
|
||||
asm volatile("sti");
|
||||
offset_print("Interrupts enabled\n");
|
||||
|
||||
offset_print("Hello, world\n");
|
||||
}
|
||||
|
||||
95
src/pic.c
Normal file
95
src/pic.c
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "pic.h"
|
||||
#include "port_io.h"
|
||||
|
||||
#define PIC1_COMMAND 0x20
|
||||
#define PIC1_DATA 0x21
|
||||
#define PIC2_COMMAND 0xA0
|
||||
#define PIC2_DATA 0xA1
|
||||
|
||||
#define ICW1_ICW4 0x01 /* ICW4 (not) needed */
|
||||
#define ICW1_SINGLE 0x02 /* Single (cascade) mode */
|
||||
#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */
|
||||
#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */
|
||||
#define ICW1_INIT 0x10 /* Initialization - required! */
|
||||
|
||||
#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */
|
||||
#define ICW4_AUTO 0x02 /* Auto (normal) EOI */
|
||||
#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */
|
||||
#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */
|
||||
#define ICW4_SFNM 0x10 /* Special fully nested (not) */
|
||||
|
||||
#define PIC_EOI 0x20
|
||||
|
||||
void pic_send_eoi(uint8_t irq)
|
||||
{
|
||||
if(irq >= 8)
|
||||
outb(PIC2_COMMAND, PIC_EOI);
|
||||
|
||||
outb(PIC1_COMMAND, PIC_EOI);
|
||||
}
|
||||
|
||||
/*
|
||||
reinitialize the PIC controllers, giving them specified vector offsets
|
||||
rather than 8h and 70h, as configured by default
|
||||
*/
|
||||
#define PIC1_OFFSET 0x20
|
||||
#define PIC2_OFFSET 0x28
|
||||
|
||||
void init_pic(void)
|
||||
{
|
||||
unsigned char a1, a2;
|
||||
|
||||
a1 = inb(PIC1_DATA); // save masks
|
||||
a2 = inb(PIC2_DATA);
|
||||
|
||||
outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); // starts the initialization sequence (in cascade mode)
|
||||
io_wait();
|
||||
outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4);
|
||||
io_wait();
|
||||
|
||||
outb(PIC1_DATA, PIC1_OFFSET); // ICW2: Master PIC vector offset
|
||||
io_wait();
|
||||
outb(PIC2_DATA, PIC2_OFFSET); // ICW2: Slave PIC vector offset
|
||||
io_wait();
|
||||
|
||||
outb(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100)
|
||||
io_wait();
|
||||
outb(PIC2_DATA, 2); // ICW3: tell Slave PIC its cascade identity (0000 0010)
|
||||
io_wait();
|
||||
|
||||
outb(PIC1_DATA, ICW4_8086);
|
||||
io_wait();
|
||||
outb(PIC2_DATA, ICW4_8086);
|
||||
io_wait();
|
||||
|
||||
outb(PIC1_DATA, a1); // restore saved masks.
|
||||
outb(PIC2_DATA, a2);
|
||||
}
|
||||
|
||||
void pic_clear_mask(uint8_t irq) {
|
||||
uint16_t port;
|
||||
uint8_t value;
|
||||
|
||||
if(irq < 8) {
|
||||
port = PIC1_DATA;
|
||||
} else {
|
||||
port = PIC2_DATA;
|
||||
irq -= 8;
|
||||
}
|
||||
value = inb(port) & ~(1 << irq);
|
||||
outb(port, value);
|
||||
}
|
||||
|
||||
void pic_set_mask(uint8_t irq) {
|
||||
uint16_t port;
|
||||
uint8_t value;
|
||||
|
||||
if(irq < 8) {
|
||||
port = PIC1_DATA;
|
||||
} else {
|
||||
port = PIC2_DATA;
|
||||
irq -= 8;
|
||||
}
|
||||
value = inb(port) | (1 << irq);
|
||||
outb(port, value);
|
||||
}
|
||||
11
src/pic.h
Normal file
11
src/pic.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef PIC_H
|
||||
#define PIC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void init_pic(void);
|
||||
void pic_send_eoi(uint8_t irq);
|
||||
void pic_clear_mask(uint8_t irq);
|
||||
void pic_set_mask(uint8_t irq);
|
||||
|
||||
#endif
|
||||
25
src/port_io.h
Normal file
25
src/port_io.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef PORT_IO_H
|
||||
#define PORT_IO_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static inline void outb(uint16_t port, uint8_t val)
|
||||
{
|
||||
asm volatile ( "outb %b0, %w1" : : "a"(val), "Nd"(port) );
|
||||
}
|
||||
|
||||
static inline uint8_t inb(uint16_t port)
|
||||
{
|
||||
uint8_t ret;
|
||||
asm volatile ( "inb %w1, %b0" : "=a"(ret) : "Nd"(port) );
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void io_wait(void)
|
||||
{
|
||||
/* Port 0x80 is used for 'checkpoints' during POST. */
|
||||
/* The Linux kernel seems to think it is free for use :-/ */
|
||||
asm volatile ( "outb %%al, $0x80" : : "a"(0) );
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user