Add interrupts and keyboard driver

This commit is contained in:
2025-11-20 21:13:50 +01:00
parent 2d52c45915
commit 18092face9
19 changed files with 1259 additions and 7 deletions

View File

@@ -37,6 +37,7 @@ python3 scripts/embed_js.py build/index.js > "$BUILD_DIR/embedded_js.h"
# Compiler flags # Compiler flags
CFLAGS="-m32 -march=i686 -ffreestanding -nostdlib -fno-builtin" CFLAGS="-m32 -march=i686 -ffreestanding -nostdlib -fno-builtin"
CFLAGS="$CFLAGS -mno-sse -mno-sse2 -mno-mmx -mno-3dnow"
CFLAGS="$CFLAGS -I$PICOLIBC_INSTALL/include" CFLAGS="$CFLAGS -I$PICOLIBC_INSTALL/include"
CFLAGS="$CFLAGS -I./lib/duktape/src" CFLAGS="$CFLAGS -I./lib/duktape/src"
CFLAGS="$CFLAGS -I./src/lib" CFLAGS="$CFLAGS -I./src/lib"
@@ -56,10 +57,19 @@ echo "=== Building kernel with picolibc ==="
echo "Assembling boot code..." echo "Assembling boot code..."
nasm -f elf32 src/boot/kernel.asm -o "$BUILD_DIR/kasm.o" nasm -f elf32 src/boot/kernel.asm -o "$BUILD_DIR/kasm.o"
# Build interrupt assembly
echo "Assembling interrupt handlers..."
nasm -f elf32 src/kernel/interrupt/interrupt.asm -o "$BUILD_DIR/interrupt.o"
# Build duktape # Build duktape
echo "Building Duktape..." echo "Building Duktape..."
gcc $CFLAGS -c lib/duktape/src/duktape.c -o "$BUILD_DIR/duktape.o" gcc $CFLAGS -c lib/duktape/src/duktape.c -o "$BUILD_DIR/duktape.o"
# Build interrupt system
echo "Building interrupt system..."
gcc $CFLAGS -c src/kernel/interrupt/idt.c -o "$BUILD_DIR/idt.o"
gcc $CFLAGS -c src/kernel/interrupt/isr.c -o "$BUILD_DIR/isr.o"
# Build kernel # Build kernel
echo "Building kernel..." echo "Building kernel..."
gcc $CFLAGS -c src/kernel/kernel.c -o "$BUILD_DIR/kc.o" gcc $CFLAGS -c src/kernel/kernel.c -o "$BUILD_DIR/kc.o"
@@ -72,6 +82,9 @@ gcc $CFLAGS -c src/lib/syscalls.c -o "$BUILD_DIR/syscalls.o"
echo "Linking kernel..." echo "Linking kernel..."
ld $LDFLAGS -T src/link.ld -o "$OUT_DIR/kernel" \ ld $LDFLAGS -T src/link.ld -o "$OUT_DIR/kernel" \
"$BUILD_DIR/kasm.o" \ "$BUILD_DIR/kasm.o" \
"$BUILD_DIR/interrupt.o" \
"$BUILD_DIR/idt.o" \
"$BUILD_DIR/isr.o" \
"$BUILD_DIR/kc.o" \ "$BUILD_DIR/kc.o" \
"$BUILD_DIR/duktape.o" \ "$BUILD_DIR/duktape.o" \
"$BUILD_DIR/syscalls.o" \ "$BUILD_DIR/syscalls.o" \

View File

@@ -8,10 +8,18 @@ section .text
global start global start
extern kmain ;kmain is defined in the kernel.c file extern kmain ;kmain is defined in the kernel.c file
extern __stack_top
start: start:
cli ; stop interrupts cli ; stop interrupts during boot
mov esp, __stack_top
; Initialize FPU (x87)
finit ; Initialize FPU to default state
call kmain call kmain
; Note: kmain will call sti to enable interrupts after IDT is set up
hlt ; halt the CPU hang:
hlt ; halt the CPU
jmp hang

View File

@@ -0,0 +1,22 @@
#include "idt.h"
/* Define the IDT and IDT register here */
idt_gate_t idt[IDT_ENTRIES];
idt_register_t idt_reg;
void set_idt_gate(int n, u32 handler)
{
idt[n].low_offset = low_16(handler);
idt[n].sel = KERNEL_CS;
idt[n].always0 = 0;
idt[n].flags = 0x8E;
idt[n].high_offset = high_16(handler);
}
void set_idt()
{
idt_reg.base = (u32)&idt;
idt_reg.limit = IDT_ENTRIES * sizeof(idt_gate_t) - 1;
/* Don't make the mistake of loading &idt -- always load &idt_reg */
__asm__ __volatile__("lidtl (%0)" : : "r"(&idt_reg));
}

View File

@@ -0,0 +1,40 @@
#ifndef IDT_H
#define IDT_H
#include "types.h"
/* Segment selectors */
#define KERNEL_CS 0x08
/* How every interrupt gate (handler) is defined */
typedef struct
{
u16 low_offset; /* Lower 16 bits of handler function address */
u16 sel; /* Kernel segment selector */
u8 always0;
/* First byte
* Bit 7: "Interrupt is present"
* Bits 6-5: Privilege level of caller (0=kernel..3=user)
* Bit 4: Set to 0 for interrupt gates
* Bits 3-0: bits 1110 = decimal 14 = "32 bit interrupt gate" */
u8 flags;
u16 high_offset; /* Higher 16 bits of handler function address */
} __attribute__((packed)) idt_gate_t;
/* A pointer to the array of interrupt handlers.
* Assembly instruction 'lidt' will read it */
typedef struct
{
u16 limit;
u32 base;
} __attribute__((packed)) idt_register_t;
#define IDT_ENTRIES 256
extern idt_gate_t idt[IDT_ENTRIES];
extern idt_register_t idt_reg;
/* Functions implemented in idt.c */
void set_idt_gate(int n, u32 handler);
void set_idt();
#endif

View File

@@ -0,0 +1,446 @@
; Defined in isr.c
[extern isr_handler]
[extern irq_handler]
; Common ISR code
isr_common_stub:
; 1. Save CPU state
pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
mov ax, ds ; Lower 16-bits of eax = ds.
push eax ; save the data segment descriptor
mov ax, 0x10 ; kernel data segment descriptor
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; 2. Call C handler - pass pointer to registers_t struct on stack
push esp
call isr_handler
add esp, 4
; 3. Restore state
pop eax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
popa
add esp, 8 ; Cleans up the pushed error code and pushed ISR number
iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP (and restores interrupt flag)
; Common IRQ code
irq_common_stub:
; 1. Save CPU state
pusha
mov ax, ds
push eax
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; 2. Call C handler - pass pointer to registers_t struct on stack
push esp
call irq_handler
add esp, 4
; 3. Restore state
pop ebx
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
popa
add esp, 8
iret ; iret restores the interrupt flag automatically from saved EFLAGS
; We don't get information about which interrupt was caller
; when the handler is run, so we will need to have a different handler
; for every interrupt.
; Furthermore, some interrupts push an error code onto the stack but others
; don't, so we will push a dummy error code for those which don't, so that
; we have a consistent stack for all of them.
; First make the ISRs global
global isr0
global isr1
global isr2
global isr3
global isr4
global isr5
global isr6
global isr7
global isr8
global isr9
global isr10
global isr11
global isr12
global isr13
global isr14
global isr15
global isr16
global isr17
global isr18
global isr19
global isr20
global isr21
global isr22
global isr23
global isr24
global isr25
global isr26
global isr27
global isr28
global isr29
global isr30
global isr31
; 0: Divide By Zero Exception
isr0:
cli
push byte 0
push byte 0
jmp isr_common_stub
; 1: Debug Exception
isr1:
cli
push byte 0
push byte 1
jmp isr_common_stub
; 2: Non Maskable Interrupt Exception
isr2:
cli
push byte 0
push byte 2
jmp isr_common_stub
; 3: Int 3 Exception
isr3:
cli
push byte 0
push byte 3
jmp isr_common_stub
; 4: INTO Exception
isr4:
cli
push byte 0
push byte 4
jmp isr_common_stub
; 5: Out of Bounds Exception
isr5:
cli
push byte 0
push byte 5
jmp isr_common_stub
; 6: Invalid Opcode Exception
isr6:
cli
push byte 0
push byte 6
jmp isr_common_stub
; 7: Coprocessor Not Available Exception
isr7:
cli
push byte 0
push byte 7
jmp isr_common_stub
; 8: Double Fault Exception (With Error Code!)
isr8:
cli
push byte 8
jmp isr_common_stub
; 9: Coprocessor Segment Overrun Exception
isr9:
cli
push byte 0
push byte 9
jmp isr_common_stub
; 10: Bad TSS Exception (With Error Code!)
isr10:
cli
push byte 10
jmp isr_common_stub
; 11: Segment Not Present Exception (With Error Code!)
isr11:
cli
push byte 11
jmp isr_common_stub
; 12: Stack Fault Exception (With Error Code!)
isr12:
cli
push byte 12
jmp isr_common_stub
; 13: General Protection Fault Exception (With Error Code!)
isr13:
cli
push byte 13
jmp isr_common_stub
; 14: Page Fault Exception (With Error Code!)
isr14:
cli
push byte 14
jmp isr_common_stub
; 15: Reserved Exception
isr15:
cli
push byte 0
push byte 15
jmp isr_common_stub
; 16: Floating Point Exception
isr16:
cli
push byte 0
push byte 16
jmp isr_common_stub
; 17: Alignment Check Exception
isr17:
cli
push byte 0
push byte 17
jmp isr_common_stub
; 18: Machine Check Exception
isr18:
cli
push byte 0
push byte 18
jmp isr_common_stub
; 19: Reserved
isr19:
cli
push byte 0
push byte 19
jmp isr_common_stub
; 20: Reserved
isr20:
cli
push byte 0
push byte 20
jmp isr_common_stub
; 21: Reserved
isr21:
cli
push byte 0
push byte 21
jmp isr_common_stub
; 22: Reserved
isr22:
cli
push byte 0
push byte 22
jmp isr_common_stub
; 23: Reserved
isr23:
cli
push byte 0
push byte 23
jmp isr_common_stub
; 24: Reserved
isr24:
cli
push byte 0
push byte 24
jmp isr_common_stub
; 25: Reserved
isr25:
cli
push byte 0
push byte 25
jmp isr_common_stub
; 26: Reserved
isr26:
cli
push byte 0
push byte 26
jmp isr_common_stub
; 27: Reserved
isr27:
cli
push byte 0
push byte 27
jmp isr_common_stub
; 28: Reserved
isr28:
cli
push byte 0
push byte 28
jmp isr_common_stub
; 29: Reserved
isr29:
cli
push byte 0
push byte 29
jmp isr_common_stub
; 30: Reserved
isr30:
cli
push byte 0
push byte 30
jmp isr_common_stub
; 31: Reserved
isr31:
cli
push byte 0
push byte 31
jmp isr_common_stub
; IRQ handlers
global irq0
global irq1
global irq2
global irq3
global irq4
global irq5
global irq6
global irq7
global irq8
global irq9
global irq10
global irq11
global irq12
global irq13
global irq14
global irq15
; IRQ 0: System Timer
irq0:
cli
push byte 0
push byte 32
jmp irq_common_stub
; IRQ 1: Keyboard
irq1:
cli
push byte 0
push byte 33
jmp irq_common_stub
; IRQ 2: Cascade (never raised)
irq2:
cli
push byte 0
push byte 34
jmp irq_common_stub
; IRQ 3: COM2
irq3:
cli
push byte 0
push byte 35
jmp irq_common_stub
; IRQ 4: COM1
irq4:
cli
push byte 0
push byte 36
jmp irq_common_stub
; IRQ 5: LPT2
irq5:
cli
push byte 0
push byte 37
jmp irq_common_stub
; IRQ 6: Floppy Disk
irq6:
cli
push byte 0
push byte 38
jmp irq_common_stub
; IRQ 7: LPT1
irq7:
cli
push byte 0
push byte 39
jmp irq_common_stub
; IRQ 8: CMOS Real-time Clock
irq8:
cli
push byte 0
push byte 40
jmp irq_common_stub
; IRQ 9: Free for peripherals
irq9:
cli
push byte 0
push byte 41
jmp irq_common_stub
; IRQ 10: Free for peripherals
irq10:
cli
push byte 0
push byte 42
jmp irq_common_stub
; IRQ 11: Free for peripherals
irq11:
cli
push byte 0
push byte 43
jmp irq_common_stub
; IRQ 12: PS/2 Mouse
irq12:
cli
push byte 0
push byte 44
jmp irq_common_stub
; IRQ 13: FPU
irq13:
cli
push byte 0
push byte 45
jmp irq_common_stub
; IRQ 14: Primary ATA Hard Disk
irq14:
cli
push byte 0
push byte 46
jmp irq_common_stub
; IRQ 15: Secondary ATA Hard Disk
irq15:
cli
push byte 0
push byte 47
jmp irq_common_stub

317
src/kernel/interrupt/isr.c Normal file
View File

@@ -0,0 +1,317 @@
#include "isr.h"
#include "idt.h"
/* Port I/O functions */
static inline void outb(u16 port, u8 val)
{
__asm__ volatile("outb %0, %1" : : "a"(val), "Nd"(port));
}
static inline u8 inb(u16 port)
{
u8 ret;
__asm__ volatile("inb %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
/* PIC (Programmable Interrupt Controller) constants */
#define PIC1_COMMAND 0x20
#define PIC1_DATA 0x21
#define PIC2_COMMAND 0xA0
#define PIC2_DATA 0xA1
#define PIC_EOI 0x20
/* Array to store custom IRQ handlers */
static irq_handler_t irq_handlers[16] = {0};
/* Can't do this with a loop because we need the address
* of the function names */
void isr_install()
{
set_idt_gate(0, (u32)isr0);
set_idt_gate(1, (u32)isr1);
set_idt_gate(2, (u32)isr2);
set_idt_gate(3, (u32)isr3);
set_idt_gate(4, (u32)isr4);
set_idt_gate(5, (u32)isr5);
set_idt_gate(6, (u32)isr6);
set_idt_gate(7, (u32)isr7);
set_idt_gate(8, (u32)isr8);
set_idt_gate(9, (u32)isr9);
set_idt_gate(10, (u32)isr10);
set_idt_gate(11, (u32)isr11);
set_idt_gate(12, (u32)isr12);
set_idt_gate(13, (u32)isr13);
set_idt_gate(14, (u32)isr14);
set_idt_gate(15, (u32)isr15);
set_idt_gate(16, (u32)isr16);
set_idt_gate(17, (u32)isr17);
set_idt_gate(18, (u32)isr18);
set_idt_gate(19, (u32)isr19);
set_idt_gate(20, (u32)isr20);
set_idt_gate(21, (u32)isr21);
set_idt_gate(22, (u32)isr22);
set_idt_gate(23, (u32)isr23);
set_idt_gate(24, (u32)isr24);
set_idt_gate(25, (u32)isr25);
set_idt_gate(26, (u32)isr26);
set_idt_gate(27, (u32)isr27);
set_idt_gate(28, (u32)isr28);
set_idt_gate(29, (u32)isr29);
set_idt_gate(30, (u32)isr30);
set_idt_gate(31, (u32)isr31);
set_idt(); // Load with ASM
}
/* Remap the PIC to avoid conflicts with CPU exceptions */
static void pic_remap(void)
{
// Start initialization
outb(PIC1_COMMAND, 0x11);
outb(PIC2_COMMAND, 0x11);
// Set vector offsets (IRQs 0-7 -> interrupts 32-39, IRQs 8-15 -> interrupts 40-47)
outb(PIC1_DATA, 0x20);
outb(PIC2_DATA, 0x28);
// Tell master PIC there's a slave PIC at IRQ2
outb(PIC1_DATA, 0x04);
// Tell slave PIC its cascade identity
outb(PIC2_DATA, 0x02);
// Set to 8086 mode
outb(PIC1_DATA, 0x01);
outb(PIC2_DATA, 0x01);
// Mask all interrupts initially
outb(PIC1_DATA, 0xFF);
outb(PIC2_DATA, 0xFF);
}
/* Install IRQ handlers */
void irq_install()
{
// Remap the PIC
pic_remap();
// Install IRQ handlers (32-47)
set_idt_gate(32, (u32)irq0);
set_idt_gate(33, (u32)irq1);
set_idt_gate(34, (u32)irq2);
set_idt_gate(35, (u32)irq3);
set_idt_gate(36, (u32)irq4);
set_idt_gate(37, (u32)irq5);
set_idt_gate(38, (u32)irq6);
set_idt_gate(39, (u32)irq7);
set_idt_gate(40, (u32)irq8);
set_idt_gate(41, (u32)irq9);
set_idt_gate(42, (u32)irq10);
set_idt_gate(43, (u32)irq11);
set_idt_gate(44, (u32)irq12);
set_idt_gate(45, (u32)irq13);
set_idt_gate(46, (u32)irq14);
set_idt_gate(47, (u32)irq15);
set_idt(); // Reload IDT
}
/* To print the message which defines every exception */
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 *r)
{
// Write exception to VGA memory
char *vidmem = (char *)0xb8000;
const char *msg = "EXCEPTION: ";
int i = 0;
// Clear first line
for (i = 0; i < 80 * 2; i++)
{
vidmem[i] = 0;
}
// Write message
i = 0;
while (msg[i])
{
vidmem[i * 2] = msg[i];
vidmem[i * 2 + 1] = 0x4F; // White on red
i++;
}
// Write exception number
vidmem[i * 2] = '0' + (r->int_no / 10);
vidmem[i * 2 + 1] = 0x4F;
i++;
vidmem[i * 2] = '0' + (r->int_no % 10);
vidmem[i * 2 + 1] = 0x4F;
i++;
// Write error code
vidmem[i * 2] = ' ';
vidmem[i * 2 + 1] = 0x4F;
i++;
vidmem[i * 2] = 'E';
vidmem[i * 2 + 1] = 0x4F;
i++;
vidmem[i * 2] = 'R';
vidmem[i * 2 + 1] = 0x4F;
i++;
vidmem[i * 2] = 'R';
vidmem[i * 2 + 1] = 0x4F;
i++;
vidmem[i * 2] = ':';
vidmem[i * 2 + 1] = 0x4F;
i++;
// Write error code in hex
u32 err = r->err_code;
for (int j = 7; j >= 0; j--)
{
u8 nibble = (err >> (j * 4)) & 0xF;
vidmem[i * 2] = nibble < 10 ? '0' + nibble : 'A' + (nibble - 10);
vidmem[i * 2 + 1] = 0x4F;
i++;
}
// Write EIP
i++;
vidmem[i * 2] = 'E';
vidmem[i * 2 + 1] = 0x4F;
i++;
vidmem[i * 2] = 'I';
vidmem[i * 2 + 1] = 0x4F;
i++;
vidmem[i * 2] = 'P';
vidmem[i * 2 + 1] = 0x4F;
i++;
vidmem[i * 2] = ':';
vidmem[i * 2 + 1] = 0x4F;
i++;
// Write EIP in hex
u32 eip = r->eip;
for (int j = 7; j >= 0; j--)
{
u8 nibble = (eip >> (j * 4)) & 0xF;
vidmem[i * 2] = nibble < 10 ? '0' + nibble : 'A' + (nibble - 10);
vidmem[i * 2 + 1] = 0x4F;
i++;
}
// Halt
__asm__ volatile("cli; hlt");
while (1)
;
}
/* IRQ handler */
void irq_handler(registers_t *r)
{
// Calculate IRQ number (interrupts 32-47 map to IRQ 0-15)
u8 irq_no = r->int_no - 32;
// Handle spurious IRQ7 (from master PIC)
if (irq_no == 7)
{
// Read In-Service Register (ISR)
outb(PIC1_COMMAND, 0x0B);
u8 isr = inb(PIC1_COMMAND);
if (!(isr & 0x80))
{
// Spurious interrupt, don't send EOI
return;
}
}
// Handle spurious IRQ15 (from slave PIC)
if (irq_no == 15)
{
// Read In-Service Register (ISR) of slave PIC
outb(PIC2_COMMAND, 0x0B);
u8 isr = inb(PIC2_COMMAND);
if (!(isr & 0x80))
{
// Spurious interrupt from slave, send EOI to master only
outb(PIC1_COMMAND, PIC_EOI);
return;
}
}
// Call custom handler if registered
if (irq_handlers[irq_no] != 0)
{
irq_handlers[irq_no](r);
}
// Send EOI to PIC
if (r->int_no >= 40)
{
// IRQ came from slave PIC, send EOI to both
outb(PIC2_COMMAND, PIC_EOI);
}
outb(PIC1_COMMAND, PIC_EOI);
}
/* Register a custom IRQ handler */
void irq_register_handler(u8 irq, irq_handler_t handler)
{
if (irq < 16)
{
irq_handlers[irq] = handler;
// Unmask the IRQ on the PIC
u16 port;
u8 value;
if (irq < 8)
{
port = PIC1_DATA;
}
else
{
port = PIC2_DATA;
irq -= 8;
}
value = inb(port) & ~(1 << irq);
outb(port, value);
}
}

View File

@@ -0,0 +1,78 @@
#ifndef ISR_H
#define ISR_H
#include "types.h"
/* ISRs reserved for CPU exceptions */
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();
/* IRQ handlers */
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();
/* Struct which aggregates many registers */
typedef struct
{
u32 ds; /* Data segment selector */
u32 edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */
u32 int_no, err_code; /* Interrupt number and error code (if applicable) */
u32 eip, cs, eflags, useresp, ss; /* Pushed by the processor automatically */
} registers_t;
/* Function pointer type for IRQ handlers */
typedef void (*irq_handler_t)(registers_t *);
void isr_install();
void isr_handler(registers_t *r);
void irq_install();
void irq_handler(registers_t *r);
/* Register a custom IRQ handler */
void irq_register_handler(u8 irq, irq_handler_t handler);
#endif

View File

@@ -0,0 +1,16 @@
#ifndef TYPES_H
#define TYPES_H
/* Instead of using 'chars' to allocate non-character bytes,
* we will use these new type with no semantic meaning */
typedef unsigned int u32;
typedef int s32;
typedef unsigned short u16;
typedef short s16;
typedef unsigned char u8;
typedef char s8;
#define low_16(address) (u16)((address) & 0xFFFF)
#define high_16(address) (u16)(((address) >> 16) & 0xFFFF)
#endif

View File

@@ -1,7 +1,9 @@
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <duktape.h> #include <duktape.h>
#include "embedded_js.h" #include "embedded_js.h"
#include "interrupt/isr.h"
#define WHITE_TXT 0x0F #define WHITE_TXT 0x0F
@@ -56,6 +58,35 @@ duk_ret_t native_ptout(duk_context *ctx)
return 0; return 0;
} }
duk_ret_t native_byte_in(duk_context *ctx)
{
// Get the port (first argument)
uint16_t port = (uint16_t)duk_to_uint32(ctx, 0);
uint8_t result;
__asm__ volatile("inb %1, %0"
: "=a"(result)
: "Nd"(port));
duk_push_uint(ctx, (duk_uint_t)result);
return 1;
}
duk_ret_t native_byte_out(duk_context *ctx)
{
// Get the port (first argument)
uint16_t port = (uint16_t)duk_to_uint32(ctx, 0);
// Get the value to write (second argument)
uint8_t value = (uint8_t)duk_to_uint32(ctx, 1);
__asm__ volatile("outb %0, %1"
:
: "a"(value), "Nd"(port));
return 0;
}
duk_ret_t native_dword_in(duk_context *ctx) duk_ret_t native_dword_in(duk_context *ctx)
{ {
// Get the port (first argument) // Get the port (first argument)
@@ -85,10 +116,90 @@ duk_ret_t native_dword_out(duk_context *ctx)
return 0; return 0;
} }
// Global context for IRQ handlers
duk_context *global_ctx = NULL;
// JavaScript IRQ wrapper
void js_irq_callback(registers_t *r)
{
if (global_ctx == NULL)
return;
// Get IRQ number
u8 irq_no = r->int_no - 32;
// Build the key for this IRQ handler manually (avoid snprintf in interrupt)
char key[32] = "irq_handler_";
int key_len = 12;
if (irq_no >= 10)
{
key[key_len++] = '0' + (irq_no / 10);
key[key_len++] = '0' + (irq_no % 10);
}
else
{
key[key_len++] = '0' + irq_no;
}
key[key_len] = '\0';
// Call the JavaScript handler
duk_push_global_stash(global_ctx);
if (duk_get_prop_string(global_ctx, -1, key))
{
// Function found, call it with IRQ number
duk_push_uint(global_ctx, irq_no);
// Try protected call with error handler
duk_int_t rc = duk_pcall(global_ctx, 1);
duk_pop(global_ctx); // Pop result or error
}
else
{
duk_pop(global_ctx); // Pop undefined value
}
duk_pop(global_ctx); // Pop stash
}
// Native function to register IRQ from JavaScript
duk_ret_t native_irq_register(duk_context *ctx)
{
// Get the IRQ number
u8 irq = (u8)duk_to_uint32(ctx, 0);
// Get the handler function
if (!duk_is_function(ctx, 1))
{
duk_push_boolean(ctx, 0);
return 1;
}
// Store function in global stash
char key[32];
snprintf(key, sizeof(key), "irq_handler_%d", irq);
duk_push_global_stash(ctx);
duk_dup(ctx, 1); // Duplicate the function
duk_put_prop_string(ctx, -2, key);
duk_pop(ctx); // Pop stash
// Register the C wrapper
irq_register_handler(irq, js_irq_callback);
duk_push_boolean(ctx, 1);
return 1;
}
void kmain() void kmain()
{ {
// Initialize IDT and ISRs
isr_install();
irq_install();
// Initialize Duktape heap // Initialize Duktape heap
ctx = duk_create_heap_default(); ctx = duk_create_heap_default();
global_ctx = ctx;
// Register native memory write function // Register native memory write function
duk_push_c_function(ctx, native_addrw, 2); duk_push_c_function(ctx, native_addrw, 2);
@@ -102,6 +213,14 @@ void kmain()
duk_push_c_function(ctx, native_ptout, 2); duk_push_c_function(ctx, native_ptout, 2);
duk_put_global_string(ctx, "$ptout"); duk_put_global_string(ctx, "$ptout");
// Register native byte in function
duk_push_c_function(ctx, native_byte_in, 1);
duk_put_global_string(ctx, "$bytein");
// Register native byte out function
duk_push_c_function(ctx, native_byte_out, 2);
duk_put_global_string(ctx, "$byteout");
// Register native dword in function // Register native dword in function
duk_push_c_function(ctx, native_dword_in, 1); duk_push_c_function(ctx, native_dword_in, 1);
duk_put_global_string(ctx, "$dwordin"); duk_put_global_string(ctx, "$dwordin");
@@ -110,6 +229,13 @@ void kmain()
duk_push_c_function(ctx, native_dword_out, 2); duk_push_c_function(ctx, native_dword_out, 2);
duk_put_global_string(ctx, "$dwordout"); duk_put_global_string(ctx, "$dwordout");
// Register native IRQ registration function
duk_push_c_function(ctx, native_irq_register, 2);
duk_put_global_string(ctx, "$irqregister");
// Enable interrupts before JavaScript execution
__asm__ volatile("sti");
// Execute embedded JavaScript code from build/index.js // Execute embedded JavaScript code from build/index.js
duk_push_string(ctx, embedded_js_code); duk_push_string(ctx, embedded_js_code);
duk_int_t returnCode = duk_peval(ctx); duk_int_t returnCode = duk_peval(ctx);

View File

@@ -0,0 +1,57 @@
export const Keycode = {
Q: 16,
W: 17,
E: 18,
R: 19,
T: 20,
Y: 21,
U: 22,
I: 23,
O: 24,
P: 25,
A: 30,
S: 31,
D: 32,
F: 33,
G: 34,
H: 35,
J: 36,
K: 37,
L: 38,
Z: 44,
X: 45,
C: 46,
V: 47,
B: 48,
N: 49,
M: 50,
};
export const RKeycode = {
16: "Q",
17: "W",
18: "E",
19: "R",
20: "T",
21: "Y",
22: "U",
23: "I",
24: "O",
25: "P",
30: "A",
31: "S",
32: "D",
33: "F",
34: "G",
35: "H",
36: "J",
37: "K",
38: "L",
44: "Z",
45: "X",
46: "C",
47: "V",
48: "B",
49: "N",
50: "M",
};

View File

@@ -0,0 +1,29 @@
import { Keycode, RKeycode } from "../../../config/keyboard";
import { Logger } from "../../../lib/libstd/logger/logger.kmod";
import { bytein } from "../../../lib/libts/byte";
import { kmod_cpu_irq_register } from "../../modules/cpu/interrupts";
import { kmod_terminal_input_handleKeyboardInput } from "../../modules/terminal/input";
export function kdriver_dev_keyboard_handleInterrupt(): void {
const scancode = bytein(0x60);
const key = RKeycode[scancode as keyof typeof RKeycode] as
| keyof typeof Keycode
| undefined;
if (key == undefined) return;
kmod_terminal_input_handleKeyboardInput(key);
}
export function kdriver_dev_keyboard_init(): void {
if (!kmod_cpu_irq_register(1, kdriver_dev_keyboard_handleInterrupt, null)) {
Logger.log("[keyboard] Failed to register keyboard interrupt");
return;
}
let key = 0;
while ((key = bytein(0x64) & 1) == 1) {
bytein(0x60);
}
}

View File

@@ -22,6 +22,10 @@ import {
kmod_graphics_vga_init, kmod_graphics_vga_init,
kmod_graphics_vga_pushLine, kmod_graphics_vga_pushLine,
} from "./modules/graphics/graphics.kmod"; } from "./modules/graphics/graphics.kmod";
import {
kmod_terminal_input_init,
kmod_terminal_input_onKeyboardInput,
} from "./modules/terminal/input";
export function kmain() { export function kmain() {
kmod_drivers_init(); kmod_drivers_init();
@@ -33,14 +37,17 @@ export function kmain() {
kmod_filesystem_init(); kmod_filesystem_init();
kmod_filesystem_mount("/sys", sysfs_driver); kmod_filesystem_mount("/sys", sysfs_driver);
Logger.log("[Kernel] Initializing terminal module...");
kmod_terminal_input_init();
Logger.log("[Kernel] Initializing PCI devices..."); Logger.log("[Kernel] Initializing PCI devices...");
kdriver_dev_pci_detectDevices(); kdriver_dev_pci_detectDevices();
Logger.log("[Kernel] Initializing VGA module..."); Logger.log("[Kernel] Initializing VGA module...");
kmod_graphics_vga_init(); kmod_graphics_vga_init();
kmod_graphics_vga_pushLine("[Kernel] Kernel initialized successfully."); kmod_graphics_vga_pushLine("[Kernel] Kernel initialized successfully.");
kmod_graphics_vga_pushLine("Current date: " + getDate().toDateString());
kmod_graphics_vga_pushLine( kmod_terminal_input_onKeyboardInput(function (keycode) {
Number(kmod_filesystem_readFile("/sys/pci/0:1:0/vendor")).toString(16) kmod_graphics_vga_pushLine(keycode);
); });
} }

View File

@@ -0,0 +1,43 @@
import { Logger } from "../../../lib/libstd/logger/logger.kmod";
import type { KInterrupt, KInterruptHandler } from "../../../types/interrupt";
const kmod_cpu_irq_data: KInterrupt[] = [];
export function kmod_cpu_irq_register(
irq: number,
handler: KInterruptHandler,
data: unknown
): boolean {
if (kmod_cpu_irq_data[irq]) {
Logger.log("[IRQ] IRQ " + irq + " is already registered.");
return false;
}
if (irq > 15) {
Logger.log("[IRQ] IRQ " + irq + " is out of bounds.");
return false;
}
kmod_cpu_irq_data[irq] = {
handler: handler,
data: data,
};
function wrapper(irqNum: number) {
const irqData = kmod_cpu_irq_data[irqNum];
if (irqData) {
irqData.handler(irqNum, irqData.data);
}
}
const success = $irqregister(irq, wrapper);
if (!success) {
Logger.log("[IRQ] Failed to register IRQ " + irq + " with hardware.");
delete kmod_cpu_irq_data[irq];
return false;
}
Logger.log("[IRQ] Successfully registered IRQ " + irq);
return true;
}

View File

@@ -1,7 +1,12 @@
import { kdriver_dev_keyboard_init } from "../../drivers/dev/keyboard";
import { kdriver_dev_pci_init } from "../../drivers/dev/pci"; import { kdriver_dev_pci_init } from "../../drivers/dev/pci";
import { kdriver_etc_serial_init } from "../../drivers/etc/serial"; import { kdriver_etc_serial_init } from "../../drivers/etc/serial";
const drivers = [kdriver_etc_serial_init, kdriver_dev_pci_init]; const drivers = [
kdriver_etc_serial_init,
kdriver_dev_pci_init,
kdriver_dev_keyboard_init,
];
export function kmod_drivers_init(): void {} export function kmod_drivers_init(): void {}

View File

@@ -0,0 +1,21 @@
import type { Keycode } from "../../../config/keyboard";
const kmod_terminal_input_keyboardInputHandlers: Array<
(keycode: keyof typeof Keycode) => void
> = [];
export function kmod_terminal_input_init(): void {}
export function kmod_terminal_input_onKeyboardInput(
handler: (keycode: keyof typeof Keycode) => void
) {
kmod_terminal_input_keyboardInputHandlers.push(handler);
}
export function kmod_terminal_input_handleKeyboardInput(
keycode: keyof typeof Keycode
): void {
for (let i = 0; i < kmod_terminal_input_keyboardInputHandlers.length; i++) {
kmod_terminal_input_keyboardInputHandlers[i]!(keycode);
}
}

View File

@@ -0,0 +1 @@
export * from "./input";

View File

@@ -1,3 +1,11 @@
export function bytein(port: number): number {
return $bytein(port);
}
export function byteout(port: number, value: number): void {
$byteout(port, value);
}
export function charc(char: string): number { export function charc(char: string): number {
return char.charCodeAt(0); return char.charCodeAt(0);
} }

View File

@@ -1,5 +1,11 @@
declare function $addrw(address: number, value: number): void; declare function $addrw(address: number, value: number): void;
declare function $ptin(port: number): number; declare function $ptin(port: number): number;
declare function $ptout(port: number, value: number): void; declare function $ptout(port: number, value: number): void;
declare function $bytein(port: number): number;
declare function $byteout(port: number, value: number): void;
declare function $dwordin(port: number): number; declare function $dwordin(port: number): number;
declare function $dwordout(port: number, value: number): void; declare function $dwordout(port: number, value: number): void;
declare const $irqregister: (
irq: number,
handler: (irqNum: number) => void
) => boolean;

View File

@@ -0,0 +1,9 @@
export type KInterruptHandler = (
interruptNumber: number,
data?: unknown
) => void;
export type KInterrupt = {
handler: KInterruptHandler;
data: unknown;
};