Move to kernel/
diff --git a/src/kernel/.gdbinit b/src/kernel/.gdbinit
new file mode 100644
index 0000000..24efe1e
--- /dev/null
+++ b/src/kernel/.gdbinit
@@ -0,0 +1 @@
+target remote localhost:1234
diff --git a/src/kernel/Makefile b/src/kernel/Makefile
new file mode 100644
index 0000000..f369294
--- /dev/null
+++ b/src/kernel/Makefile
@@ -0,0 +1,51 @@
+SOURCES = 	boot.o \
+			main.o \
+			descriptor_tables.o \
+			io.o \
+			vga.o \
+			gdt_flush.o \
+			idt.o \
+			log.o \
+			irq.o \
+			pic.o \
+			timer.o \
+			paging.o \
+			switch_table.o \
+			scan_codes.o
+CFLAGS = -nostdlib -nostdinc -fno-builtin -fno-stack-protector -ffreestanding -m32 -O2 -g
+LDFLAGS = -Tlink.ld -melf_i386
+ASMFLAGS = -felf
+QEMUFLAGS = -d cpu_reset
+
+JAYROOT = ../../
+
+kernel.elf: $(SOURCES)
+	ld $(LDFLAGS) -o $@ $^
+
+clean:
+	rm -f *.o *.bin *.elf $(JAYROOT)/bin/*.iso
+
+debug: kernel.elf
+	qemu-system-i386 -s -S -kernel kernel.elf &
+	@echo run "target remote localhost:1234" to connect to qemu
+	gdb
+	@pkill qemu-system-i38
+
+qemu: kernel.elf
+	qemu-system-i386 $(QEMUFLAGS) -monitor stdio -kernel kernel.elf -no-reboot
+
+qemu-iso: install
+	qemu-system-i386 $(QEMUFLAGS) -monitor stdio $(JAYROOT)/bin/bluejay.iso
+
+scan_codes.c: gen_scan_codes.py scan_codes.tsv 
+	python3 $< > $@
+
+.s.o:
+	nasm $(ASMFLAGS) $<
+
+install: kernel.elf
+	cp kernel.elf ../boot/
+	rm -f $(JAYROOT)/bin/bluejay.iso
+	grub-mkrescue -o $(JAYROOT)/bin/bluejay.iso ..
+
+.PHONY: install qemu clean qemu-iso debug
diff --git a/src/kernel/boot.s b/src/kernel/boot.s
new file mode 100644
index 0000000..c71ef40
--- /dev/null
+++ b/src/kernel/boot.s
@@ -0,0 +1,37 @@
+;;; GRUB Multiboot header, calls main() in main.c
+
+MBOOT_PAGE_ALIGN    equ 1<<0
+MBOOT_MEM_INFO      equ 1<<1
+MBOOT_HEADER_MAGIC  equ 0x1BADB002
+
+MBOOT_HEADER_FLAGS  equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO
+MBOOT_CHECKSUM      equ -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS)
+
+
+	[bits 32]
+
+	[global mboot]
+	[extern code]
+	[extern bss]
+	[extern end]
+
+mboot:
+	dd  MBOOT_HEADER_MAGIC ; This tells GRUB to start executing here:
+	dd  MBOOT_HEADER_FLAGS
+	dd  MBOOT_CHECKSUM
+   
+	dd  mboot                   ; Current location
+	dd  code                    ; .text section
+	dd  bss                     ; End of .data
+	dd  end                     ; End of kernel
+	dd  start
+
+	[global start]
+	[extern kmain]				; C code
+
+start:
+	push ebx					; Holds multiboot header location
+
+	cli
+	call kmain
+	jmp $
diff --git a/src/kernel/descriptor_tables.c b/src/kernel/descriptor_tables.c
new file mode 100644
index 0000000..916a1ae
--- /dev/null
+++ b/src/kernel/descriptor_tables.c
@@ -0,0 +1,111 @@
+#include "descriptor_tables.h"
+#include "io.h"
+#include "log.h"
+#include "pic.h"
+#include "vga.h"
+
+extern void gdt_flush(uint gdt);
+extern void idt_flush(uint idt);
+
+static void gdt_set_gate(uint i, uint base, uint limit, uchar access,
+						 uchar gran);
+static void idt_set_gate(uchar num, uint base, ushort selector, uchar flags);
+
+struct gdt_entry gdt_entries[5];
+struct gdt_pointer gdt_pointer;
+
+struct idt_entry idt_entries[256];
+struct idt_pointer idt_pointer;
+
+static void (*isrs[32])() = {
+	isr0,  isr1,  isr2,	 isr3,	isr4,  isr5,  isr6,	 isr7,	isr8,  isr9,  isr10,
+	isr11, isr12, isr13, isr14, isr15, isr16, isr17, isr18, isr19, isr20, isr21,
+	isr22, isr23, isr24, isr25, isr26, isr27, isr28, isr29, isr30, isr31};
+
+static void (*irqs[16])() = {irq0,	irq1,  irq2,  irq3, irq4,  irq5,
+							 irq6,	irq7,  irq8,  irq9, irq10, irq11,
+							 irq12, irq13, irq14, irq15};
+
+extern void (*interrupt_handlers[256])(struct registers);
+
+void init_gdt()
+{
+	vga_write("Initializing GDT...\n");
+	gdt_pointer.limit = sizeof(struct gdt_entry) * 5 - 1;
+	gdt_pointer.base = (uint)&gdt_entries;
+
+	gdt_set_gate(0, 0, 0, 0, 0);		// Null segment
+	gdt_set_gate(1, 0, ~0, 0x9a, 0xcf); // Code segment
+	gdt_set_gate(2, 0, ~0, 0x92, 0xcf); // Data segment
+	gdt_set_gate(3, 0, ~0, 0xfa, 0xcf); // User mode code segment
+	gdt_set_gate(4, 0, ~0, 0xf2, 0xcf); // User mode data segment
+
+	for (volatile uint i = 0; i < 0x1000; i++)
+	{
+	} // waste some time, for some reason this helps
+
+	gdt_flush((uint)&gdt_pointer);
+
+	vga_write("GDT Initialized\n");
+}
+
+static void gdt_set_gate(uint i, uint base, uint limit, uchar access,
+						 uchar gran)
+{
+	struct gdt_entry *e = &gdt_entries[i];
+
+	e->base_low = base & 0xffff;
+	e->base_middle = (base >> 16) & 0xff;
+	e->base_high = (base >> 24) & 0xff;
+
+	e->limit_low = limit & 0xffff;
+	e->granularity = ((limit >> 16) & 0x0f) | (gran & 0xf0);
+
+	e->access = access;
+}
+
+void init_idt()
+{
+	idt_pointer.limit = sizeof(struct idt_entry) * 256 - 1;
+	idt_pointer.base = (uint)&idt_entries;
+
+	memset(&idt_entries, 0, sizeof(idt_entries));
+
+	// Remap PIC
+	pic_remap();
+
+	vga_set_color(CYAN, BLACK);
+	for (int i = 0; i < 16; i++)
+	{
+		idt_set_gate(IRQ_TO_INT(i), (uint)irqs[i], 0x08, 0x8e);
+	}
+	vga_set_color(WHITE, BLACK);
+
+	for (int i = 0; i < 32; i++)
+	{
+		idt_set_gate(i, (uint)isrs[i], 0x08, 0x8e);
+	}
+
+	idt_flush((uint)&idt_pointer);
+
+	vga_write("IDT Initialized!\n");
+}
+
+static void idt_set_gate(uchar num, uint base, ushort selector, uchar flags)
+{
+	struct idt_entry *e = &idt_entries[num];
+
+	e->base_low = base & 0xffff;
+	e->base_high = (base >> 16) & 0xffff;
+
+	e->selector = selector;
+	e->zero = 0;
+	e->flags = flags /* | 0x60 */;
+}
+
+void init_descriptor_tables()
+{
+	init_gdt();
+	init_idt();
+	memset(interrupt_handlers, 0, sizeof(interrupt_handlers));
+}
diff --git a/src/kernel/descriptor_tables.h b/src/kernel/descriptor_tables.h
new file mode 100644
index 0000000..b35f670
--- /dev/null
+++ b/src/kernel/descriptor_tables.h
@@ -0,0 +1,126 @@
+#pragma once
+
+#include "kint.h"
+
+struct gdt_entry
+{
+	ushort limit_low;
+	ushort base_low;
+	uchar base_middle;
+
+	union {
+		struct
+		{
+			uint a_p : 1;
+			uint a_dpl : 2;
+			uint a_dt : 1;
+			uint a_type : 4;
+		} __attribute__((packed));
+
+		uchar access;
+	};
+
+	union {
+		struct
+		{
+			uint g_g : 1;
+			uint g_d : 1;
+			uint g_zero : 2; /* includes A */
+			uint g_len : 4;
+		} __attribute__((packed));
+
+		uchar granularity;
+	};
+
+	uchar base_high;
+} __attribute__((packed));
+
+struct gdt_pointer
+{
+	/* Upper 16 bits of selector limits */
+	ushort limit;
+	/* first struct gdt_entry */
+	uint base;
+} __attribute__((packed));
+
+struct idt_entry
+{
+	ushort base_low;
+	ushort selector;
+	uchar zero;
+
+	union {
+		struct
+		{
+			uchar f_p : 1;
+			uchar f_dpl : 2;
+			uchar f_const : 5;
+		} __attribute__((packed));
+
+		uchar flags;
+	};
+
+	ushort base_high;
+} __attribute__((packed));
+
+#define IDT_F_CONST 0b00110
+
+struct idt_pointer
+{
+	ushort limit;
+	uint base;
+} __attribute__((packed));
+
+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();
+
+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_descriptor_tables();
+void init_idt();
+void init_gdt();
diff --git a/src/kernel/gdt_flush.s b/src/kernel/gdt_flush.s
new file mode 100644
index 0000000..f1dd8ac
--- /dev/null
+++ b/src/kernel/gdt_flush.s
@@ -0,0 +1,17 @@
+	[bits 32]
+	[global gdt_flush]
+
+gdt_flush:
+	mov eax, [esp + 4]
+	lgdt [eax]					; Load GDT
+
+	mov ax, 0x10				; Offset of data segment
+	mov ds, ax,
+	mov es, ax
+	mov fs, ax
+	mov gs, ax
+	mov ss, ax
+
+	jmp 0x08:.flush				; Implicitly reloads the code segment
+.flush:
+	ret
diff --git a/src/kernel/gen_scan_codes.py b/src/kernel/gen_scan_codes.py
new file mode 100644
index 0000000..6bf68c9
--- /dev/null
+++ b/src/kernel/gen_scan_codes.py
@@ -0,0 +1,48 @@
+# Generate scan_codes.h from scan_codes.tsv
+
+from functools import reduce
+
+def symbolify(s):
+    if not s.startswith("'"):
+        return s.replace('+', 'PLUS').replace('*', 'TIMES').replace('/', 'DIV').replace('-', 'MINUS').replace('.', 'DOT')
+    else: return s
+
+def gen_set_entry(set, line):
+    parts = line.split('\t')
+
+    make_break = parts[set]
+    [make, brk] = [[int(y, 16) for y in x.split(' ')] for x in make_break.replace('(base)', '').strip().split('/')]
+    brk_pfx = brk[0]
+    if not set == 1:
+        brk = brk[1:]
+
+    if make[0] == 0xe0 or make[0] == 0xe1:
+        return
+
+    [make, brk] = [reduce(lambda o, n: o << 8 | n, x, 0) for x in [make, brk]]
+
+    ascii = str(parts[6] == 'yes').lower()
+    symbol = symbolify(parts[7])
+    upper_symbol = symbolify(parts[8])
+    
+    print(f"""\t[{make}] = {'{'} .ascii = {ascii}, .symbol = {symbol},
+\t\t.up_symbol = {upper_symbol}, .prefix = {brk_pfx} {'}'},""")
+
+    if set == 1:
+        print(f"""\t[{brk}] = {'{'} .ascii = {ascii}, .symbol = {symbol},
+\t\t.up_symbol = {upper_symbol}, .prefix = {brk_pfx}, .brk = true {'}'},""")
+
+def gen_set(lines, set):
+    # NOTE will only work with scan code set 3
+    print("const struct kbd_scan_codes scan_code_set_" + str(set) + "[0xff] =\n{")
+    for l in lines:
+        gen_set_entry(set, l)
+    print("};")
+
+with open('scan_codes.tsv', 'r') as f:
+    lines = f.read().split('\n')[1:]
+    print('#include "kbd.h"\n\n')
+    gen_set(lines, 1)
+    print('\n')
+    gen_set(lines, 3)
+
diff --git a/src/kernel/idt.s b/src/kernel/idt.s
new file mode 100644
index 0000000..3e2ce5d
--- /dev/null
+++ b/src/kernel/idt.s
@@ -0,0 +1,84 @@
+	[bits 32]
+	[global idt_flush]
+
+idt_flush:
+	mov eax, [esp + 4]
+	lidt [eax]
+	ret
+
+%macro ISRNOERR 1
+	[global isr%1]
+isr%1:
+	cli
+	push 0
+	push %1
+	jmp isr_common
+%endmacro
+
+%macro ISRERR 1
+	[global isr%1]
+isr%1:
+	cli
+	push byte %1
+	jmp isr_common
+%endmacro
+
+ISRNOERR 0
+ISRNOERR 1
+ISRNOERR 2
+ISRNOERR 3
+ISRNOERR 4
+ISRNOERR 5
+ISRNOERR 6
+ISRNOERR 7
+ISRERR 8
+ISRNOERR 9
+ISRERR 10
+ISRERR 11
+ISRERR 12
+ISRERR 13
+ISRERR 14
+ISRNOERR 15
+ISRNOERR 16
+ISRNOERR 17
+ISRNOERR 18
+ISRNOERR 19
+ISRNOERR 20
+ISRNOERR 21
+ISRNOERR 22
+ISRNOERR 23
+ISRNOERR 24
+ISRNOERR 25
+ISRNOERR 26
+ISRNOERR 27
+ISRNOERR 28
+ISRNOERR 29
+ISRNOERR 30
+ISRNOERR 31
+
+
+	[extern isr_handler]
+isr_common:
+	pusha						; Save all registers
+
+	mov ax, ds					; Save data segment
+	push eax
+
+	mov ax, 0x10 				; New segments
+	mov ds, ax
+	mov es, ax
+	mov fs, ax
+	mov gs, ax
+
+	call isr_handler
+
+	pop eax						; Reset segments
+	mov ds, ax
+	mov es, ax
+	mov fs, ax
+	mov gs, ax
+
+	popa
+	add esp, 8 					; Passed arguments
+	sti
+	iret						; Return from interrupt
diff --git a/src/kernel/io.c b/src/kernel/io.c
new file mode 100644
index 0000000..55bf7a7
--- /dev/null
+++ b/src/kernel/io.c
@@ -0,0 +1,91 @@
+#include "io.h"
+#include "kbd.h"
+#include "log.h"
+#include "pic.h"
+
+bool pressed_keys[LAST_KBD_KEY];
+char special_key_mappings[LAST_KBD_KEY - FIRST_KBD_KEY] =
+{
+	[KBD_ENTER - FIRST_KBD_KEY] = '\n',
+	[KBD_SPACEBAR - FIRST_KBD_KEY] = ' ',
+	[KBD_TAB - FIRST_KBD_KEY] = '\t',
+	[KBD_BACKSPACE - FIRST_KBD_KEY] = '\b',
+	0
+};
+
+void outb(ushort port, uchar val)
+{
+	asm volatile("outb %1, %0" : : "dN"(port), "a"(val));
+}
+
+uchar inb(ushort port)
+{
+	uchar ret;
+	asm volatile("inb %1, %0" : "=a"(ret) : "dN"(port));
+	return ret;
+}
+
+ushort inw(ushort port)
+{
+	ushort ret;
+	asm volatile("inw %1, %0" : "=a"(ret) : "dN"(port));
+	return ret;
+}
+
+void *memset(void *s, int c, size_t n)
+{
+	for (size_t i = 0; i < n; i++)
+	{
+		((uchar *)s)[i] = c;
+	}
+	return s;
+}
+
+void *memcpy(void *dest, const void *src, size_t n)
+{
+	for (size_t i = 0; i < n; i++)
+	{
+		((uchar *)dest)[i] = ((uchar *)src)[i];
+	}
+	return dest;
+}
+
+void io_wait()
+{
+	asm volatile("outb %0, $0x80" ::"a"(0));
+}
+
+uchar kbd_scan_code()
+{
+	return inb(KBD_DATA_PORT);
+}
+
+static bool kbd_shift_pressed()
+{
+	return pressed_keys[KBD_LEFT_SHIFT] || pressed_keys[KBD_RIGHT_SHIFT] ||
+		   pressed_keys[KBD_CAPS_LOCK];
+}
+
+void kbd_handle_input(struct registers *registers)
+{
+	uchar byte = kbd_scan_code();
+
+	struct kbd_scan_codes code = scan_code_set_1[byte];
+
+	if (code.ascii && !code.brk)
+	{
+		kprintf("%c", kbd_shift_pressed() && code.up_symbol ? code.up_symbol
+															: code.symbol);
+	}
+	else if (!code.brk && special_key_mappings[code.symbol - FIRST_KBD_KEY])
+	{
+		kprintf("%c", special_key_mappings[code.symbol - FIRST_KBD_KEY]);
+	}
+	pressed_keys[code.symbol] = !code.brk;
+}
+
+void init_kbd()
+{
+	memset(pressed_keys, 0, LAST_KBD_KEY);
+	add_interrupt_handler(33, kbd_handle_input);
+}
diff --git a/src/kernel/io.h b/src/kernel/io.h
new file mode 100644
index 0000000..28af387
--- /dev/null
+++ b/src/kernel/io.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "kint.h"
+#include "registers.h"
+
+#define KBD_CMD_PORT 0x64
+#define KBD_DATA_PORT 0x60
+
+struct kbd_scan_code_info
+{
+	bool pressed;
+	bool escape;
+	char key;
+};
+
+extern struct kbd_scan_code_info scan_code_table[0xff];
+
+void outb(ushort port, uchar val);
+uchar inb(ushort port);
+ushort inw(ushort port);
+
+void *memset(void *s, int c, size_t n);
+void *memcpy(void *dest, const void *src, size_t n);
+
+void io_wait();
+
+uchar kbd_scan_code();
+void kbd_handle_input(struct registers *registers);
+void init_kbd();
diff --git a/src/kernel/irq.s b/src/kernel/irq.s
new file mode 100644
index 0000000..e340a61
--- /dev/null
+++ b/src/kernel/irq.s
@@ -0,0 +1,53 @@
+	[bits 32]
+
+%macro IRQ 2
+	[global irq%1]
+irq%1:
+	cli
+	push 0		 				; Error code
+	push %2						; Interrupt number
+	jmp irq_common
+%endmacro
+
+IRQ 0, 32
+IRQ 1, 33
+IRQ 2, 34
+IRQ 3, 35
+IRQ 4, 36
+IRQ 5, 37
+IRQ 6, 38
+IRQ 7, 39
+IRQ 8, 40
+IRQ 9, 41
+IRQ 10, 42
+IRQ 11, 43
+IRQ 12, 44
+IRQ 13, 45
+IRQ 14, 46
+IRQ 15, 47
+
+	[extern irq_handler]
+irq_common:
+	pusha
+	mov ax, ds					; Save data segment
+	push eax
+
+	mov ax, 0x10 				; New segments
+	mov ds, ax
+	mov es, ax
+	mov fs, ax
+	mov gs, ax
+
+	call irq_handler
+
+	pop ebx						; Old data segment
+	mov ds, bx
+	mov es, bx
+	mov fs, bx
+	mov gs, bx
+
+	popa
+	add esp, 8
+	sti
+	iret
+
diff --git a/src/kernel/kbd.h b/src/kernel/kbd.h
new file mode 100644
index 0000000..98d6fd7
--- /dev/null
+++ b/src/kernel/kbd.h
@@ -0,0 +1,77 @@
+#pragma once
+
+#include "kint.h"
+
+struct kbd_scan_codes
+{
+	bool ascii;
+	int symbol;
+	int up_symbol;
+	uchar prefix;
+	bool brk;
+};
+
+enum kbd_keys
+{
+	FIRST_KBD_KEY = 129,
+	KBD_BACKSPACE = 129,
+	KBD_TAB,
+	KBD_CAPS_LOCK,
+	KBD_ENTER,
+	KBD_LEFT_SHIFT,
+	KBD_RIGHT_SHIFT,
+	KBD_LEFT_CTRL,
+	KBD_LEFT_ALT,
+	KBD_SPACEBAR,
+	KBD_RIGHT_ALT,
+	KBD_RIGHT_CTRL,
+	KBD_INSERT,
+	KBD_DELETE,
+	KBD_LEFT_ARROW,
+	KBD_HOME,
+	KBD_END,
+	KBD_UP_ARROW,
+	KBD_DOWN_ARROW,
+	KBD_RIGHT_ARROW,
+	KBD_NUM_LOCK,
+	KBD_KEYPAD_0,
+	KBD_KEYPAD_1,
+	KBD_KEYPAD_2,
+	KBD_KEYPAD_3,
+	KBD_KEYPAD_4,
+	KBD_KEYPAD_5,
+	KBD_KEYPAD_6,
+	KBD_KEYPAD_7,
+	KBD_KEYPAD_8,
+	KBD_KEYPAD_9,
+	KBD_KEYPAD_DOT,
+	KBD_KEYPAD_PLUS,
+	KBD_KEYPAD_MINUS,
+	KBD_KEYPAD_TIMES,
+	KBD_KEYPAD_DIV,
+	KBD_KEYPAD_ENTER,
+	KBD_ESC,
+	KBD_F1,
+	KBD_F2,
+	KBD_F3,
+	KBD_F4,
+	KBD_F5,
+	KBD_F6,
+	KBD_F7,
+	KBD_F8,
+	KBD_F9,
+	KBD_F10,
+	KBD_F11,
+	KBD_F12,
+	KBD_PRINT_SCREEN,
+	KBD_SCROLL_LOCK,
+	KBD_PAUSE_BREAK,
+	KBD_PAGE_UP,
+	KBD_PAGE_DOWN,
+	LAST_KBD_KEY,
+};
+
+extern const struct kbd_scan_codes scan_code_set_3[0xff];
+extern const struct kbd_scan_codes scan_code_set_1[0xff];
+extern bool pressed_keys[LAST_KBD_KEY];
+extern char special_key_mappings[LAST_KBD_KEY - FIRST_KBD_KEY];
diff --git a/src/kernel/kint.h b/src/kernel/kint.h
new file mode 100644
index 0000000..44b9bfd
--- /dev/null
+++ b/src/kernel/kint.h
@@ -0,0 +1,18 @@
+#pragma once
+
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+
+typedef unsigned long size_t;
+
+typedef uchar bool;
+
+enum
+{
+	false = 0,
+	true,
+};
+
+#define NULL 0
diff --git a/src/kernel/link.ld b/src/kernel/link.ld
new file mode 100644
index 0000000..2abbaeb
--- /dev/null
+++ b/src/kernel/link.ld
@@ -0,0 +1,27 @@
+ENTRY(start)
+SECTIONS
+{
+    .text 0x100000 :
+    {
+        code = .; _code = .; __code = .;
+        *(.text)
+        . = ALIGN(4096);
+    }
+
+    .data :
+    {
+        data = .; _data = .; __data = .;
+        *(.data)
+        *(.rodata)
+        . = ALIGN(4096);
+    }
+
+    .bss :
+    {
+        bss = .; _bss = .; __bss = .;
+        *(.bss)
+        . = ALIGN(4096);
+    }
+
+    end = .; _end = .; __end = .;
+}
diff --git a/src/kernel/log.c b/src/kernel/log.c
new file mode 100644
index 0000000..ac49971
--- /dev/null
+++ b/src/kernel/log.c
@@ -0,0 +1,68 @@
+#include "log.h"
+#include "kint.h"
+#include "stdarg.h"
+#include "vga.h"
+
+void kprintf(const char *format, ...)
+{
+	va_list args;
+	va_start(args, format);
+
+	while (*format)
+	{
+		if (*format == '%')
+		{
+			format++;
+
+			switch (*format)
+			{
+			case 'd': {
+				uint x = (uint)va_arg(args, uint);
+				vga_putd(x);
+				break;
+			}
+
+			case 'x': {
+				// consider hex always unsigned
+				uint x = (uint)va_arg(args, uint);
+				vga_putx(x);
+				break;
+			}
+
+			case 's': {
+				char *s = va_arg(args, char *);
+				vga_write(s);
+				break;
+			}
+
+			case 'c': {
+				char s = va_arg(args, int);
+				vga_put(s);
+				break;
+			}
+			}
+			format++;
+		}
+		else
+		{
+			vga_put(*format);
+			format++;
+		}
+	}
+
+	va_end(args);
+}
+
+void kassert_int(bool condition, const char *message, const char *file,
+				 const int line)
+{
+	if (!condition)
+	{
+		vga_set_color(LIGHT_RED, BLACK);
+		kprintf("ASSERTION FAILED: %s:%d\n%s\n", file, line, message);
+
+		while (1)
+		{
+		}
+	}
+}
diff --git a/src/kernel/log.h b/src/kernel/log.h
new file mode 100644
index 0000000..fe5dcd9
--- /dev/null
+++ b/src/kernel/log.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "kint.h"
+
+void kprintf(const char *format, ...);
+void kassert_int(bool condition, const char *message, const char *file,
+				 const int line);
+
+#define kassert(cond, msg) kassert_int((cond), (msg), __FILE__, __LINE__)
+#define kpanic(msg)                                                            \
+	kassert(false, msg);                                                       \
+	__builtin_unreachable()
diff --git a/src/kernel/main.c b/src/kernel/main.c
new file mode 100644
index 0000000..15b6c09
--- /dev/null
+++ b/src/kernel/main.c
@@ -0,0 +1,41 @@
+#include "descriptor_tables.h"
+#include "io.h"
+#include "log.h"
+#include "timer.h"
+#include "vga.h"
+#include "paging.h"
+
+int kmain(void *mboot)
+{
+	vga_clear();
+	vga_set_color(LIGHT_BLUE, BLACK);
+	vga_write("Hello!\nWelcome to Bluejay OS\n");
+	vga_set_color(WHITE, BLACK);
+
+	init_descriptor_tables();
+
+	vga_set_color(LIGHT_GREEN, BLACK);
+	vga_write("Setup complete!\n");
+	vga_set_color(WHITE, BLACK);
+
+	init_timer(20);
+
+	initialize_paging();
+	init_kbd();
+	asm volatile("sti");
+
+
+#ifdef TEST_PAGE_FAULT
+	kprintf("Causing page fault:\n");
+
+	volatile uint *ptr = (uint *) 0xa0000000;
+	volatile uint cause_page_fault = *ptr;
+
+	kprintf("Should have caused page fault: %d...\n", cause_page_fault);
+#endif
+
+	while (true)
+		asm volatile("hlt");
+
+	return 0xCAFEBABE;
+}
diff --git a/src/kernel/paging.c b/src/kernel/paging.c
new file mode 100644
index 0000000..128de4e
--- /dev/null
+++ b/src/kernel/paging.c
@@ -0,0 +1,142 @@
+#include "paging.h"
+#include "io.h"
+#include "kint.h"
+#include "log.h"
+#include "pic.h"
+
+extern uint end;
+static size_t alloc_base = (size_t)&end;
+
+/* frames bitset, 0 = free, 1 = used */
+static uint *frames;
+static ulong num_frames;
+
+static uint first_page_table[1024] __attribute__((aligned(4096)));
+static uint *page_directory[1024] __attribute__((aligned(4096)));
+
+static uint ***current_directory;
+
+void *_kmalloc(size_t size, bool align, void **phys)
+{
+	if (align && (alloc_base & 0xfff)) // if not yet aligned
+	{
+		alloc_base &= ~0xfff;
+		alloc_base += 0x1000;
+	}
+
+	if (phys)
+	{
+		*phys = (void *)alloc_base;
+	}
+
+	size_t addr = alloc_base;
+	alloc_base += size;
+	return (void *)addr;
+}
+
+void *kmalloc(size_t size)
+{
+	return _kmalloc(size, false, NULL);
+}
+
+void *kmalloc_a(size_t size)
+{
+	return _kmalloc(size, true, NULL);
+}
+
+void *kmalloc_ap(size_t size, void **p)
+{
+	return _kmalloc(size, true, p);
+}
+
+/* frame utils */
+
+#define BITS 32
+
+static void set_frame(size_t frame_addr)
+{
+	uint frame = frame_addr / 0x1000; // page aligned
+	frames[frame / BITS] |= 1 << (frame % BITS);
+}
+
+static bool test_frame(size_t frame_addr)
+{
+	uint frame = frame_addr / 0x1000; // page aligned
+	return frames[frame / BITS] & 1 << (frame % BITS);
+}
+
+static void clear_frame(size_t frame_addr)
+{
+	uint frame = frame_addr / 0x1000; // page aligned
+	frames[frame / BITS] &= ~(1 << (frame % BITS));
+}
+
+static uint first_free_frame()
+{
+	for (int i = 0; i < num_frames / BITS; i++)
+	{
+		/*
+		 * If there are any zeroes, ~ will yield a non-zero result,
+		 * meaning that there are pages free. Otherwise, check next set
+		 */
+		if (!~frames[i])
+			continue;
+
+		for (int j = 0; j < BITS; j++)
+		{
+			if ((frames[i] & 1 << j) == 0)
+			{
+				/* found unused frame */
+				return i * BITS + j;
+			}
+		}
+	}
+
+	/* did not find a free frame, panic */
+	kpanic("first_free_frame failed! no free frames");
+}
+
+void alloc_frame(uint *page, bool for_kernel, bool writable)
+{
+	if (*page >> 12)
+		return; /* frame already allocated */
+
+	uint frame = first_free_frame();
+	//	kprintf("first_free_frame found %d\n", frame);
+	set_frame(frame * 0x1000); /* mark as mapped */
+}
+
+void free_frame(uint page)
+{
+	clear_frame(page / 0x1000);
+}
+
+/* paging stuff */
+
+void initialize_paging()
+{
+	for (int i = 0; i < 1024; i++)
+	{
+		page_directory[i] = (uint *) 0b010; // kernel, rw, not present
+	}
+
+	for (int i = 0; i < 1024; i++)
+	{
+		first_page_table[i] = (i * 0x1000) | 3;
+	}
+
+	page_directory[0] = (uint *) (((size_t) (uint *) first_page_table) | 3);
+
+	kprintf("Set up page directory[0], %x\n", (uint)first_page_table);
+
+	load_page_directory((uint) page_directory);
+	enable_paging();
+
+	add_interrupt_handler(14, page_fault);
+}
+
+void page_fault(struct registers *regs)
+{
+	kprintf("Page fault! eip = %d\n", regs->eip);
+	kpanic("Page fault");
+}
diff --git a/src/kernel/paging.h b/src/kernel/paging.h
new file mode 100644
index 0000000..7c5000e
--- /dev/null
+++ b/src/kernel/paging.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "kint.h"
+#include "registers.h"
+
+/* defined in switch_table.s */
+extern uint load_page_directory(uint table_address);
+extern void enable_paging();
+
+void *_kmalloc(size_t size, bool align, void **phys);
+void *kmalloc(size_t size);
+void *kmalloc_a(size_t size);
+void *kmalloc_ap(size_t size, void **p);
+
+void initialize_paging();
+
+void page_fault(struct registers *regs);
diff --git a/src/kernel/pic.c b/src/kernel/pic.c
new file mode 100644
index 0000000..1c96543
--- /dev/null
+++ b/src/kernel/pic.c
@@ -0,0 +1,64 @@
+#include "pic.h"
+#include "io.h"
+#include "log.h"
+
+#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) */
+
+void (*interrupt_handlers[256])(struct registers *);
+
+void pic_send_eoi(uchar interrupt)
+{
+	if (interrupt >= IRQ_TO_INT(8))
+		outb(PIC2_COMMAND, PIC_EOI);
+
+	outb(PIC1_COMMAND, PIC_EOI);
+}
+
+void irq_handler(struct registers regs)
+{
+	pic_send_eoi(regs.interrupt_number);
+
+	if (interrupt_handlers[regs.interrupt_number])
+		interrupt_handlers[regs.interrupt_number](&regs);
+	else
+		kprintf("Unhandled hardware interrupt: %d, called from %d\n", regs.interrupt_number, regs.eip);
+}
+
+void isr_handler(struct registers regs)
+{
+	if (interrupt_handlers[regs.interrupt_number])
+		interrupt_handlers[regs.interrupt_number](&regs);
+	else
+		kprintf("Unhandled interrupt: %d, called from %d\n", regs.interrupt_number, regs.eip);
+}
+
+void add_interrupt_handler(uchar interrupt, void (*handler)(struct registers *))
+{
+	interrupt_handlers[interrupt] = handler;
+}
+
+void pic_remap()
+{
+	outb(0x20, 0x11);
+	outb(0xA0, 0x11);
+	outb(0x21, 0x20);
+	outb(0xA1, 0x28);
+	outb(0x21, 0x04);
+	outb(0xA1, 0x02);
+	outb(0x21, 0x01);
+	outb(0xA1, 0x01);
+	outb(0x21, 0x0);
+	outb(0xA1, 0x0);
+
+	return;
+}
diff --git a/src/kernel/pic.h b/src/kernel/pic.h
new file mode 100644
index 0000000..de11f3f
--- /dev/null
+++ b/src/kernel/pic.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "kint.h"
+#include "registers.h"
+
+#define PIC1 0x20
+#define PIC2 0xa0
+#define PIC1_COMMAND PIC1
+#define PIC1_DATA (PIC1 + 1)
+#define PIC2_COMMAND PIC2
+#define PIC2_DATA (PIC2 + 1)
+
+#define PIC_EOI 0x20 // End of input
+
+#define IRQ_TO_INT(irq) ((irq) + 32)
+
+void pic_send_eoi(uchar interrupt);
+void add_interrupt_handler(uchar interrupt, void (*handler)(struct registers *));
+
+void pic_remap();
diff --git a/src/kernel/registers.h b/src/kernel/registers.h
new file mode 100644
index 0000000..687bd0e
--- /dev/null
+++ b/src/kernel/registers.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "kint.h"
+
+struct registers
+{
+	uint ds;
+	uint edi, esi, ebp, esp, ebx, edx, ecx, eax;
+	uint interrupt_number, error_code;
+	uint eip, cs, eflags, useresp, ss;
+};
diff --git a/src/kernel/scan_codes.c b/src/kernel/scan_codes.c
new file mode 100644
index 0000000..530fd91
--- /dev/null
+++ b/src/kernel/scan_codes.c
@@ -0,0 +1,553 @@
+#include "kbd.h"
+
+
+const struct kbd_scan_codes scan_code_set_1[0xff] =
+{
+	[41] = { .ascii = true, .symbol = '`',
+		.up_symbol = '~', .prefix = 169 },
+	[169] = { .ascii = true, .symbol = '`',
+		.up_symbol = '~', .prefix = 169, .brk = true },
+	[2] = { .ascii = true, .symbol = '1',
+		.up_symbol = '!', .prefix = 130 },
+	[130] = { .ascii = true, .symbol = '1',
+		.up_symbol = '!', .prefix = 130, .brk = true },
+	[3] = { .ascii = true, .symbol = '2',
+		.up_symbol = '@', .prefix = 131 },
+	[131] = { .ascii = true, .symbol = '2',
+		.up_symbol = '@', .prefix = 131, .brk = true },
+	[4] = { .ascii = true, .symbol = '3',
+		.up_symbol = '#', .prefix = 132 },
+	[132] = { .ascii = true, .symbol = '3',
+		.up_symbol = '#', .prefix = 132, .brk = true },
+	[5] = { .ascii = true, .symbol = '4',
+		.up_symbol = '$', .prefix = 133 },
+	[133] = { .ascii = true, .symbol = '4',
+		.up_symbol = '$', .prefix = 133, .brk = true },
+	[6] = { .ascii = true, .symbol = '5',
+		.up_symbol = '%', .prefix = 134 },
+	[134] = { .ascii = true, .symbol = '5',
+		.up_symbol = '%', .prefix = 134, .brk = true },
+	[7] = { .ascii = true, .symbol = '6',
+		.up_symbol = '^', .prefix = 135 },
+	[135] = { .ascii = true, .symbol = '6',
+		.up_symbol = '^', .prefix = 135, .brk = true },
+	[8] = { .ascii = true, .symbol = '7',
+		.up_symbol = '&', .prefix = 136 },
+	[136] = { .ascii = true, .symbol = '7',
+		.up_symbol = '&', .prefix = 136, .brk = true },
+	[9] = { .ascii = true, .symbol = '8',
+		.up_symbol = '*', .prefix = 137 },
+	[137] = { .ascii = true, .symbol = '8',
+		.up_symbol = '*', .prefix = 137, .brk = true },
+	[10] = { .ascii = true, .symbol = '9',
+		.up_symbol = '(', .prefix = 138 },
+	[138] = { .ascii = true, .symbol = '9',
+		.up_symbol = '(', .prefix = 138, .brk = true },
+	[11] = { .ascii = true, .symbol = '0',
+		.up_symbol = ')', .prefix = 139 },
+	[139] = { .ascii = true, .symbol = '0',
+		.up_symbol = ')', .prefix = 139, .brk = true },
+	[12] = { .ascii = true, .symbol = '-',
+		.up_symbol = '_', .prefix = 140 },
+	[140] = { .ascii = true, .symbol = '-',
+		.up_symbol = '_', .prefix = 140, .brk = true },
+	[13] = { .ascii = true, .symbol = '=',
+		.up_symbol = '+', .prefix = 141 },
+	[141] = { .ascii = true, .symbol = '=',
+		.up_symbol = '+', .prefix = 141, .brk = true },
+	[14] = { .ascii = false, .symbol = KBD_BACKSPACE,
+		.up_symbol = NULL, .prefix = 142 },
+	[142] = { .ascii = false, .symbol = KBD_BACKSPACE,
+		.up_symbol = NULL, .prefix = 142, .brk = true },
+	[15] = { .ascii = false, .symbol = KBD_TAB,
+		.up_symbol = NULL, .prefix = 143 },
+	[143] = { .ascii = false, .symbol = KBD_TAB,
+		.up_symbol = NULL, .prefix = 143, .brk = true },
+	[16] = { .ascii = true, .symbol = 'q',
+		.up_symbol = 'Q', .prefix = 144 },
+	[144] = { .ascii = true, .symbol = 'q',
+		.up_symbol = 'Q', .prefix = 144, .brk = true },
+	[17] = { .ascii = true, .symbol = 'w',
+		.up_symbol = 'W', .prefix = 145 },
+	[145] = { .ascii = true, .symbol = 'w',
+		.up_symbol = 'W', .prefix = 145, .brk = true },
+	[18] = { .ascii = true, .symbol = 'e',
+		.up_symbol = 'E', .prefix = 146 },
+	[146] = { .ascii = true, .symbol = 'e',
+		.up_symbol = 'E', .prefix = 146, .brk = true },
+	[19] = { .ascii = true, .symbol = 'r',
+		.up_symbol = 'R', .prefix = 147 },
+	[147] = { .ascii = true, .symbol = 'r',
+		.up_symbol = 'R', .prefix = 147, .brk = true },
+	[20] = { .ascii = true, .symbol = 't',
+		.up_symbol = 'T', .prefix = 148 },
+	[148] = { .ascii = true, .symbol = 't',
+		.up_symbol = 'T', .prefix = 148, .brk = true },
+	[21] = { .ascii = true, .symbol = 'y',
+		.up_symbol = 'Y', .prefix = 149 },
+	[149] = { .ascii = true, .symbol = 'y',
+		.up_symbol = 'Y', .prefix = 149, .brk = true },
+	[22] = { .ascii = true, .symbol = 'u',
+		.up_symbol = 'U', .prefix = 150 },
+	[150] = { .ascii = true, .symbol = 'u',
+		.up_symbol = 'U', .prefix = 150, .brk = true },
+	[23] = { .ascii = true, .symbol = 'i',
+		.up_symbol = 'I', .prefix = 151 },
+	[151] = { .ascii = true, .symbol = 'i',
+		.up_symbol = 'I', .prefix = 151, .brk = true },
+	[24] = { .ascii = true, .symbol = 'o',
+		.up_symbol = 'O', .prefix = 152 },
+	[152] = { .ascii = true, .symbol = 'o',
+		.up_symbol = 'O', .prefix = 152, .brk = true },
+	[25] = { .ascii = true, .symbol = 'p',
+		.up_symbol = 'P', .prefix = 153 },
+	[153] = { .ascii = true, .symbol = 'p',
+		.up_symbol = 'P', .prefix = 153, .brk = true },
+	[26] = { .ascii = true, .symbol = '[',
+		.up_symbol = '{', .prefix = 154 },
+	[154] = { .ascii = true, .symbol = '[',
+		.up_symbol = '{', .prefix = 154, .brk = true },
+	[27] = { .ascii = true, .symbol = ']',
+		.up_symbol = '}', .prefix = 155 },
+	[155] = { .ascii = true, .symbol = ']',
+		.up_symbol = '}', .prefix = 155, .brk = true },
+	[58] = { .ascii = false, .symbol = KBD_CAPS_LOCK,
+		.up_symbol = NULL, .prefix = 186 },
+	[186] = { .ascii = false, .symbol = KBD_CAPS_LOCK,
+		.up_symbol = NULL, .prefix = 186, .brk = true },
+	[30] = { .ascii = true, .symbol = 'a',
+		.up_symbol = 'A', .prefix = 158 },
+	[158] = { .ascii = true, .symbol = 'a',
+		.up_symbol = 'A', .prefix = 158, .brk = true },
+	[31] = { .ascii = true, .symbol = 's',
+		.up_symbol = 'S', .prefix = 159 },
+	[159] = { .ascii = true, .symbol = 's',
+		.up_symbol = 'S', .prefix = 159, .brk = true },
+	[32] = { .ascii = true, .symbol = 'd',
+		.up_symbol = 'D', .prefix = 160 },
+	[160] = { .ascii = true, .symbol = 'd',
+		.up_symbol = 'D', .prefix = 160, .brk = true },
+	[33] = { .ascii = true, .symbol = 'f',
+		.up_symbol = 'F', .prefix = 161 },
+	[161] = { .ascii = true, .symbol = 'f',
+		.up_symbol = 'F', .prefix = 161, .brk = true },
+	[34] = { .ascii = true, .symbol = 'g',
+		.up_symbol = 'G', .prefix = 162 },
+	[162] = { .ascii = true, .symbol = 'g',
+		.up_symbol = 'G', .prefix = 162, .brk = true },
+	[35] = { .ascii = true, .symbol = 'h',
+		.up_symbol = 'H', .prefix = 163 },
+	[163] = { .ascii = true, .symbol = 'h',
+		.up_symbol = 'H', .prefix = 163, .brk = true },
+	[36] = { .ascii = true, .symbol = 'j',
+		.up_symbol = 'J', .prefix = 164 },
+	[164] = { .ascii = true, .symbol = 'j',
+		.up_symbol = 'J', .prefix = 164, .brk = true },
+	[37] = { .ascii = true, .symbol = 'k',
+		.up_symbol = 'K', .prefix = 165 },
+	[165] = { .ascii = true, .symbol = 'k',
+		.up_symbol = 'K', .prefix = 165, .brk = true },
+	[38] = { .ascii = true, .symbol = 'l',
+		.up_symbol = 'L', .prefix = 166 },
+	[166] = { .ascii = true, .symbol = 'l',
+		.up_symbol = 'L', .prefix = 166, .brk = true },
+	[39] = { .ascii = true, .symbol = ';',
+		.up_symbol = ':', .prefix = 167 },
+	[167] = { .ascii = true, .symbol = ';',
+		.up_symbol = ':', .prefix = 167, .brk = true },
+	[40] = { .ascii = true, .symbol = '\'',
+		.up_symbol = '"', .prefix = 168 },
+	[168] = { .ascii = true, .symbol = '\'',
+		.up_symbol = '"', .prefix = 168, .brk = true },
+	[28] = { .ascii = false, .symbol = KBD_ENTER,
+		.up_symbol = NULL, .prefix = 156 },
+	[156] = { .ascii = false, .symbol = KBD_ENTER,
+		.up_symbol = NULL, .prefix = 156, .brk = true },
+	[42] = { .ascii = false, .symbol = KBD_LEFT_SHIFT,
+		.up_symbol = NULL, .prefix = 170 },
+	[170] = { .ascii = false, .symbol = KBD_LEFT_SHIFT,
+		.up_symbol = NULL, .prefix = 170, .brk = true },
+	[44] = { .ascii = true, .symbol = 'z',
+		.up_symbol = 'Z', .prefix = 172 },
+	[172] = { .ascii = true, .symbol = 'z',
+		.up_symbol = 'Z', .prefix = 172, .brk = true },
+	[45] = { .ascii = true, .symbol = 'x',
+		.up_symbol = 'X', .prefix = 173 },
+	[173] = { .ascii = true, .symbol = 'x',
+		.up_symbol = 'X', .prefix = 173, .brk = true },
+	[46] = { .ascii = true, .symbol = 'c',
+		.up_symbol = 'C', .prefix = 174 },
+	[174] = { .ascii = true, .symbol = 'c',
+		.up_symbol = 'C', .prefix = 174, .brk = true },
+	[47] = { .ascii = true, .symbol = 'v',
+		.up_symbol = 'V', .prefix = 175 },
+	[175] = { .ascii = true, .symbol = 'v',
+		.up_symbol = 'V', .prefix = 175, .brk = true },
+	[48] = { .ascii = true, .symbol = 'b',
+		.up_symbol = 'B', .prefix = 176 },
+	[176] = { .ascii = true, .symbol = 'b',
+		.up_symbol = 'B', .prefix = 176, .brk = true },
+	[49] = { .ascii = true, .symbol = 'n',
+		.up_symbol = 'N', .prefix = 177 },
+	[177] = { .ascii = true, .symbol = 'n',
+		.up_symbol = 'N', .prefix = 177, .brk = true },
+	[50] = { .ascii = true, .symbol = 'm',
+		.up_symbol = 'M', .prefix = 178 },
+	[178] = { .ascii = true, .symbol = 'm',
+		.up_symbol = 'M', .prefix = 178, .brk = true },
+	[51] = { .ascii = true, .symbol = ',',
+		.up_symbol = '<', .prefix = 179 },
+	[179] = { .ascii = true, .symbol = ',',
+		.up_symbol = '<', .prefix = 179, .brk = true },
+	[52] = { .ascii = true, .symbol = '.',
+		.up_symbol = '>', .prefix = 180 },
+	[180] = { .ascii = true, .symbol = '.',
+		.up_symbol = '>', .prefix = 180, .brk = true },
+	[53] = { .ascii = true, .symbol = '/',
+		.up_symbol = '?', .prefix = 181 },
+	[181] = { .ascii = true, .symbol = '/',
+		.up_symbol = '?', .prefix = 181, .brk = true },
+	[54] = { .ascii = false, .symbol = KBD_RIGHT_SHIFT,
+		.up_symbol = NULL, .prefix = 182 },
+	[182] = { .ascii = false, .symbol = KBD_RIGHT_SHIFT,
+		.up_symbol = NULL, .prefix = 182, .brk = true },
+	[29] = { .ascii = false, .symbol = KBD_LEFT_CTRL,
+		.up_symbol = NULL, .prefix = 157 },
+	[157] = { .ascii = false, .symbol = KBD_LEFT_CTRL,
+		.up_symbol = NULL, .prefix = 157, .brk = true },
+	[56] = { .ascii = false, .symbol = KBD_LEFT_ALT,
+		.up_symbol = NULL, .prefix = 184 },
+	[184] = { .ascii = false, .symbol = KBD_LEFT_ALT,
+		.up_symbol = NULL, .prefix = 184, .brk = true },
+	[57] = { .ascii = false, .symbol = KBD_SPACEBAR,
+		.up_symbol = NULL, .prefix = 185 },
+	[185] = { .ascii = false, .symbol = KBD_SPACEBAR,
+		.up_symbol = NULL, .prefix = 185, .brk = true },
+	[69] = { .ascii = false, .symbol = KBD_NUM_LOCK,
+		.up_symbol = NULL, .prefix = 197 },
+	[197] = { .ascii = false, .symbol = KBD_NUM_LOCK,
+		.up_symbol = NULL, .prefix = 197, .brk = true },
+	[71] = { .ascii = false, .symbol = KBD_KEYPAD_7,
+		.up_symbol = NULL, .prefix = 199 },
+	[199] = { .ascii = false, .symbol = KBD_KEYPAD_7,
+		.up_symbol = NULL, .prefix = 199, .brk = true },
+	[75] = { .ascii = false, .symbol = KBD_KEYPAD_4,
+		.up_symbol = NULL, .prefix = 203 },
+	[203] = { .ascii = false, .symbol = KBD_KEYPAD_4,
+		.up_symbol = NULL, .prefix = 203, .brk = true },
+	[79] = { .ascii = false, .symbol = KBD_KEYPAD_1,
+		.up_symbol = NULL, .prefix = 207 },
+	[207] = { .ascii = false, .symbol = KBD_KEYPAD_1,
+		.up_symbol = NULL, .prefix = 207, .brk = true },
+	[72] = { .ascii = false, .symbol = KBD_KEYPAD_8,
+		.up_symbol = NULL, .prefix = 200 },
+	[200] = { .ascii = false, .symbol = KBD_KEYPAD_8,
+		.up_symbol = NULL, .prefix = 200, .brk = true },
+	[76] = { .ascii = false, .symbol = KBD_KEYPAD_5,
+		.up_symbol = NULL, .prefix = 204 },
+	[204] = { .ascii = false, .symbol = KBD_KEYPAD_5,
+		.up_symbol = NULL, .prefix = 204, .brk = true },
+	[80] = { .ascii = false, .symbol = KBD_KEYPAD_2,
+		.up_symbol = NULL, .prefix = 208 },
+	[208] = { .ascii = false, .symbol = KBD_KEYPAD_2,
+		.up_symbol = NULL, .prefix = 208, .brk = true },
+	[82] = { .ascii = false, .symbol = KBD_KEYPAD_0,
+		.up_symbol = NULL, .prefix = 210 },
+	[210] = { .ascii = false, .symbol = KBD_KEYPAD_0,
+		.up_symbol = NULL, .prefix = 210, .brk = true },
+	[55] = { .ascii = false, .symbol = KBD_KEYPAD_TIMES,
+		.up_symbol = NULL, .prefix = 183 },
+	[183] = { .ascii = false, .symbol = KBD_KEYPAD_TIMES,
+		.up_symbol = NULL, .prefix = 183, .brk = true },
+	[73] = { .ascii = false, .symbol = KBD_KEYPAD_9,
+		.up_symbol = NULL, .prefix = 201 },
+	[201] = { .ascii = false, .symbol = KBD_KEYPAD_9,
+		.up_symbol = NULL, .prefix = 201, .brk = true },
+	[77] = { .ascii = false, .symbol = KBD_KEYPAD_6,
+		.up_symbol = NULL, .prefix = 205 },
+	[205] = { .ascii = false, .symbol = KBD_KEYPAD_6,
+		.up_symbol = NULL, .prefix = 205, .brk = true },
+	[81] = { .ascii = false, .symbol = KBD_KEYPAD_3,
+		.up_symbol = NULL, .prefix = 209 },
+	[209] = { .ascii = false, .symbol = KBD_KEYPAD_3,
+		.up_symbol = NULL, .prefix = 209, .brk = true },
+	[83] = { .ascii = false, .symbol = KBD_KEYPAD_DOT,
+		.up_symbol = NULL, .prefix = 211 },
+	[211] = { .ascii = false, .symbol = KBD_KEYPAD_DOT,
+		.up_symbol = NULL, .prefix = 211, .brk = true },
+	[74] = { .ascii = false, .symbol = KBD_KEYPAD_MINUS,
+		.up_symbol = NULL, .prefix = 202 },
+	[202] = { .ascii = false, .symbol = KBD_KEYPAD_MINUS,
+		.up_symbol = NULL, .prefix = 202, .brk = true },
+	[78] = { .ascii = false, .symbol = KBD_KEYPAD_PLUS,
+		.up_symbol = NULL, .prefix = 206 },
+	[206] = { .ascii = false, .symbol = KBD_KEYPAD_PLUS,
+		.up_symbol = NULL, .prefix = 206, .brk = true },
+	[1] = { .ascii = false, .symbol = KBD_ESC,
+		.up_symbol = NULL, .prefix = 129 },
+	[129] = { .ascii = false, .symbol = KBD_ESC,
+		.up_symbol = NULL, .prefix = 129, .brk = true },
+	[59] = { .ascii = false, .symbol = KBD_F1,
+		.up_symbol = NULL, .prefix = 187 },
+	[187] = { .ascii = false, .symbol = KBD_F1,
+		.up_symbol = NULL, .prefix = 187, .brk = true },
+	[60] = { .ascii = false, .symbol = KBD_F2,
+		.up_symbol = NULL, .prefix = 188 },
+	[188] = { .ascii = false, .symbol = KBD_F2,
+		.up_symbol = NULL, .prefix = 188, .brk = true },
+	[61] = { .ascii = false, .symbol = KBD_F3,
+		.up_symbol = NULL, .prefix = 189 },
+	[189] = { .ascii = false, .symbol = KBD_F3,
+		.up_symbol = NULL, .prefix = 189, .brk = true },
+	[62] = { .ascii = false, .symbol = KBD_F4,
+		.up_symbol = NULL, .prefix = 190 },
+	[190] = { .ascii = false, .symbol = KBD_F4,
+		.up_symbol = NULL, .prefix = 190, .brk = true },
+	[63] = { .ascii = false, .symbol = KBD_F5,
+		.up_symbol = NULL, .prefix = 191 },
+	[191] = { .ascii = false, .symbol = KBD_F5,
+		.up_symbol = NULL, .prefix = 191, .brk = true },
+	[64] = { .ascii = false, .symbol = KBD_F6,
+		.up_symbol = NULL, .prefix = 192 },
+	[192] = { .ascii = false, .symbol = KBD_F6,
+		.up_symbol = NULL, .prefix = 192, .brk = true },
+	[65] = { .ascii = false, .symbol = KBD_F7,
+		.up_symbol = NULL, .prefix = 193 },
+	[193] = { .ascii = false, .symbol = KBD_F7,
+		.up_symbol = NULL, .prefix = 193, .brk = true },
+	[66] = { .ascii = false, .symbol = KBD_F8,
+		.up_symbol = NULL, .prefix = 194 },
+	[194] = { .ascii = false, .symbol = KBD_F8,
+		.up_symbol = NULL, .prefix = 194, .brk = true },
+	[67] = { .ascii = false, .symbol = KBD_F9,
+		.up_symbol = NULL, .prefix = 195 },
+	[195] = { .ascii = false, .symbol = KBD_F9,
+		.up_symbol = NULL, .prefix = 195, .brk = true },
+	[68] = { .ascii = false, .symbol = KBD_F10,
+		.up_symbol = NULL, .prefix = 196 },
+	[196] = { .ascii = false, .symbol = KBD_F10,
+		.up_symbol = NULL, .prefix = 196, .brk = true },
+	[87] = { .ascii = false, .symbol = KBD_F11,
+		.up_symbol = NULL, .prefix = 215 },
+	[215] = { .ascii = false, .symbol = KBD_F11,
+		.up_symbol = NULL, .prefix = 215, .brk = true },
+	[88] = { .ascii = false, .symbol = KBD_F12,
+		.up_symbol = NULL, .prefix = 216 },
+	[216] = { .ascii = false, .symbol = KBD_F12,
+		.up_symbol = NULL, .prefix = 216, .brk = true },
+	[70] = { .ascii = false, .symbol = KBD_SCROLL_LOCK,
+		.up_symbol = NULL, .prefix = 198 },
+	[198] = { .ascii = false, .symbol = KBD_SCROLL_LOCK,
+		.up_symbol = NULL, .prefix = 198, .brk = true },
+	[43] = { .ascii = true, .symbol = '\\',
+		.up_symbol = '|', .prefix = 171 },
+	[171] = { .ascii = true, .symbol = '\\',
+		.up_symbol = '|', .prefix = 171, .brk = true },
+};
+
+
+const struct kbd_scan_codes scan_code_set_3[0xff] =
+{
+	[14] = { .ascii = true, .symbol = '`',
+		.up_symbol = '~', .prefix = 240 },
+	[22] = { .ascii = true, .symbol = '1',
+		.up_symbol = '!', .prefix = 240 },
+	[30] = { .ascii = true, .symbol = '2',
+		.up_symbol = '@', .prefix = 240 },
+	[38] = { .ascii = true, .symbol = '3',
+		.up_symbol = '#', .prefix = 240 },
+	[37] = { .ascii = true, .symbol = '4',
+		.up_symbol = '$', .prefix = 240 },
+	[46] = { .ascii = true, .symbol = '5',
+		.up_symbol = '%', .prefix = 240 },
+	[54] = { .ascii = true, .symbol = '6',
+		.up_symbol = '^', .prefix = 240 },
+	[61] = { .ascii = true, .symbol = '7',
+		.up_symbol = '&', .prefix = 240 },
+	[62] = { .ascii = true, .symbol = '8',
+		.up_symbol = '*', .prefix = 240 },
+	[70] = { .ascii = true, .symbol = '9',
+		.up_symbol = '(', .prefix = 240 },
+	[69] = { .ascii = true, .symbol = '0',
+		.up_symbol = ')', .prefix = 240 },
+	[78] = { .ascii = true, .symbol = '-',
+		.up_symbol = '_', .prefix = 240 },
+	[85] = { .ascii = true, .symbol = '=',
+		.up_symbol = '+', .prefix = 240 },
+	[102] = { .ascii = false, .symbol = KBD_BACKSPACE,
+		.up_symbol = NULL, .prefix = 240 },
+	[13] = { .ascii = false, .symbol = KBD_TAB,
+		.up_symbol = NULL, .prefix = 240 },
+	[21] = { .ascii = true, .symbol = 'q',
+		.up_symbol = 'Q', .prefix = 240 },
+	[29] = { .ascii = true, .symbol = 'w',
+		.up_symbol = 'W', .prefix = 240 },
+	[36] = { .ascii = true, .symbol = 'e',
+		.up_symbol = 'E', .prefix = 240 },
+	[45] = { .ascii = true, .symbol = 'r',
+		.up_symbol = 'R', .prefix = 240 },
+	[44] = { .ascii = true, .symbol = 't',
+		.up_symbol = 'T', .prefix = 240 },
+	[53] = { .ascii = true, .symbol = 'y',
+		.up_symbol = 'Y', .prefix = 240 },
+	[60] = { .ascii = true, .symbol = 'u',
+		.up_symbol = 'U', .prefix = 240 },
+	[67] = { .ascii = true, .symbol = 'i',
+		.up_symbol = 'I', .prefix = 240 },
+	[68] = { .ascii = true, .symbol = 'o',
+		.up_symbol = 'O', .prefix = 240 },
+	[77] = { .ascii = true, .symbol = 'p',
+		.up_symbol = 'P', .prefix = 240 },
+	[84] = { .ascii = true, .symbol = '[',
+		.up_symbol = '{', .prefix = 240 },
+	[91] = { .ascii = true, .symbol = ']',
+		.up_symbol = '}', .prefix = 240 },
+	[88] = { .ascii = false, .symbol = KBD_CAPS_LOCK,
+		.up_symbol = NULL, .prefix = 240 },
+	[28] = { .ascii = true, .symbol = 'a',
+		.up_symbol = 'A', .prefix = 240 },
+	[27] = { .ascii = true, .symbol = 's',
+		.up_symbol = 'S', .prefix = 240 },
+	[35] = { .ascii = true, .symbol = 'd',
+		.up_symbol = 'D', .prefix = 240 },
+	[43] = { .ascii = true, .symbol = 'f',
+		.up_symbol = 'F', .prefix = 240 },
+	[52] = { .ascii = true, .symbol = 'g',
+		.up_symbol = 'G', .prefix = 240 },
+	[51] = { .ascii = true, .symbol = 'h',
+		.up_symbol = 'H', .prefix = 240 },
+	[59] = { .ascii = true, .symbol = 'j',
+		.up_symbol = 'J', .prefix = 240 },
+	[66] = { .ascii = true, .symbol = 'k',
+		.up_symbol = 'K', .prefix = 240 },
+	[75] = { .ascii = true, .symbol = 'l',
+		.up_symbol = 'L', .prefix = 240 },
+	[76] = { .ascii = true, .symbol = ';',
+		.up_symbol = ':', .prefix = 240 },
+	[82] = { .ascii = true, .symbol = '\'',
+		.up_symbol = '"', .prefix = 240 },
+	[90] = { .ascii = false, .symbol = KBD_ENTER,
+		.up_symbol = NULL, .prefix = 240 },
+	[18] = { .ascii = false, .symbol = KBD_LEFT_SHIFT,
+		.up_symbol = NULL, .prefix = 240 },
+	[26] = { .ascii = true, .symbol = 'z',
+		.up_symbol = 'Z', .prefix = 240 },
+	[34] = { .ascii = true, .symbol = 'x',
+		.up_symbol = 'X', .prefix = 240 },
+	[33] = { .ascii = true, .symbol = 'c',
+		.up_symbol = 'C', .prefix = 240 },
+	[42] = { .ascii = true, .symbol = 'v',
+		.up_symbol = 'V', .prefix = 240 },
+	[50] = { .ascii = true, .symbol = 'b',
+		.up_symbol = 'B', .prefix = 240 },
+	[49] = { .ascii = true, .symbol = 'n',
+		.up_symbol = 'N', .prefix = 240 },
+	[58] = { .ascii = true, .symbol = 'm',
+		.up_symbol = 'M', .prefix = 240 },
+	[65] = { .ascii = true, .symbol = ',',
+		.up_symbol = '<', .prefix = 240 },
+	[73] = { .ascii = true, .symbol = '.',
+		.up_symbol = '>', .prefix = 240 },
+	[74] = { .ascii = true, .symbol = '/',
+		.up_symbol = '?', .prefix = 240 },
+	[89] = { .ascii = false, .symbol = KBD_RIGHT_SHIFT,
+		.up_symbol = NULL, .prefix = 240 },
+	[17] = { .ascii = false, .symbol = KBD_LEFT_CTRL,
+		.up_symbol = NULL, .prefix = 240 },
+	[25] = { .ascii = false, .symbol = KBD_LEFT_ALT,
+		.up_symbol = NULL, .prefix = 240 },
+	[41] = { .ascii = false, .symbol = KBD_SPACEBAR,
+		.up_symbol = NULL, .prefix = 240 },
+	[57] = { .ascii = false, .symbol = KBD_RIGHT_ALT,
+		.up_symbol = NULL, .prefix = 240 },
+	[88] = { .ascii = false, .symbol = KBD_RIGHT_CTRL,
+		.up_symbol = NULL, .prefix = 240 },
+	[103] = { .ascii = false, .symbol = KBD_INSERT,
+		.up_symbol = NULL, .prefix = 240 },
+	[100] = { .ascii = false, .symbol = KBD_DELETE,
+		.up_symbol = NULL, .prefix = 240 },
+	[97] = { .ascii = false, .symbol = KBD_LEFT_ARROW,
+		.up_symbol = NULL, .prefix = 240 },
+	[110] = { .ascii = false, .symbol = KBD_HOME,
+		.up_symbol = NULL, .prefix = 240 },
+	[101] = { .ascii = false, .symbol = KBD_END,
+		.up_symbol = NULL, .prefix = 240 },
+	[99] = { .ascii = false, .symbol = KBD_UP_ARROW,
+		.up_symbol = NULL, .prefix = 240 },
+	[96] = { .ascii = false, .symbol = KBD_DOWN_ARROW,
+		.up_symbol = NULL, .prefix = 240 },
+	[111] = { .ascii = false, .symbol = KBD_PAGE_UP,
+		.up_symbol = NULL, .prefix = 240 },
+	[109] = { .ascii = false, .symbol = KBD_PAGE_DOWN,
+		.up_symbol = NULL, .prefix = 240 },
+	[106] = { .ascii = false, .symbol = KBD_RIGHT_ARROW,
+		.up_symbol = NULL, .prefix = 240 },
+	[118] = { .ascii = false, .symbol = KBD_NUM_LOCK,
+		.up_symbol = NULL, .prefix = 240 },
+	[108] = { .ascii = false, .symbol = KBD_KEYPAD_7,
+		.up_symbol = NULL, .prefix = 240 },
+	[107] = { .ascii = false, .symbol = KBD_KEYPAD_4,
+		.up_symbol = NULL, .prefix = 240 },
+	[105] = { .ascii = false, .symbol = KBD_KEYPAD_1,
+		.up_symbol = NULL, .prefix = 240 },
+	[119] = { .ascii = false, .symbol = KBD_KEYPAD_DIV,
+		.up_symbol = NULL, .prefix = 240 },
+	[117] = { .ascii = false, .symbol = KBD_KEYPAD_8,
+		.up_symbol = NULL, .prefix = 240 },
+	[115] = { .ascii = false, .symbol = KBD_KEYPAD_5,
+		.up_symbol = NULL, .prefix = 240 },
+	[114] = { .ascii = false, .symbol = KBD_KEYPAD_2,
+		.up_symbol = NULL, .prefix = 240 },
+	[112] = { .ascii = false, .symbol = KBD_KEYPAD_0,
+		.up_symbol = NULL, .prefix = 240 },
+	[126] = { .ascii = false, .symbol = KBD_KEYPAD_TIMES,
+		.up_symbol = NULL, .prefix = 240 },
+	[125] = { .ascii = false, .symbol = KBD_KEYPAD_9,
+		.up_symbol = NULL, .prefix = 240 },
+	[116] = { .ascii = false, .symbol = KBD_KEYPAD_6,
+		.up_symbol = NULL, .prefix = 240 },
+	[122] = { .ascii = false, .symbol = KBD_KEYPAD_3,
+		.up_symbol = NULL, .prefix = 240 },
+	[113] = { .ascii = false, .symbol = KBD_KEYPAD_DOT,
+		.up_symbol = NULL, .prefix = 240 },
+	[132] = { .ascii = false, .symbol = KBD_KEYPAD_MINUS,
+		.up_symbol = NULL, .prefix = 240 },
+	[124] = { .ascii = false, .symbol = KBD_KEYPAD_PLUS,
+		.up_symbol = NULL, .prefix = 240 },
+	[121] = { .ascii = false, .symbol = KBD_KEYPAD_ENTER,
+		.up_symbol = NULL, .prefix = 240 },
+	[8] = { .ascii = false, .symbol = KBD_ESC,
+		.up_symbol = NULL, .prefix = 240 },
+	[7] = { .ascii = false, .symbol = KBD_F1,
+		.up_symbol = NULL, .prefix = 240 },
+	[15] = { .ascii = false, .symbol = KBD_F2,
+		.up_symbol = NULL, .prefix = 240 },
+	[23] = { .ascii = false, .symbol = KBD_F3,
+		.up_symbol = NULL, .prefix = 240 },
+	[31] = { .ascii = false, .symbol = KBD_F4,
+		.up_symbol = NULL, .prefix = 240 },
+	[39] = { .ascii = false, .symbol = KBD_F5,
+		.up_symbol = NULL, .prefix = 240 },
+	[47] = { .ascii = false, .symbol = KBD_F6,
+		.up_symbol = NULL, .prefix = 240 },
+	[55] = { .ascii = false, .symbol = KBD_F7,
+		.up_symbol = NULL, .prefix = 240 },
+	[63] = { .ascii = false, .symbol = KBD_F8,
+		.up_symbol = NULL, .prefix = 240 },
+	[71] = { .ascii = false, .symbol = KBD_F9,
+		.up_symbol = NULL, .prefix = 240 },
+	[79] = { .ascii = false, .symbol = KBD_F10,
+		.up_symbol = NULL, .prefix = 240 },
+	[86] = { .ascii = false, .symbol = KBD_F11,
+		.up_symbol = NULL, .prefix = 240 },
+	[94] = { .ascii = false, .symbol = KBD_F12,
+		.up_symbol = NULL, .prefix = 240 },
+	[87] = { .ascii = false, .symbol = KBD_PRINT_SCREEN,
+		.up_symbol = NULL, .prefix = 240 },
+	[95] = { .ascii = false, .symbol = KBD_SCROLL_LOCK,
+		.up_symbol = NULL, .prefix = 240 },
+	[98] = { .ascii = false, .symbol = KBD_PAUSE_BREAK,
+		.up_symbol = NULL, .prefix = 240 },
+	[92] = { .ascii = true, .symbol = '\\',
+		.up_symbol = '|', .prefix = 240 },
+};
diff --git a/src/kernel/scan_codes.tsv b/src/kernel/scan_codes.tsv
new file mode 100644
index 0000000..d036329
--- /dev/null
+++ b/src/kernel/scan_codes.tsv
@@ -0,0 +1,102 @@
+IBM Key No.	Set 1 Make/Break	Set 2 Make/Break	Set 3 Make/Break	Base Case	Upper Case	Is ASCII char	Symbol	Uppercase Symbol

+1	29/A9	0E/F0 0E	0E/F0 0E	`	~	yes	'`'	'~'

+2	02/82	16/F0 16	16/F0 16	1	!	yes	'1'	'!'

+3	03/83	1E/F0 1E	1E/F0 1E	2	@	yes	'2'	'@'

+4	04/84	26/F0 26	26/F0 26	3	#	yes	'3'	'#'

+5	05/85	25/F0 25	25/F0 25	4	$	yes	'4'	'$'

+6	06/86	2E/F0 2E	2E/F0 2E	5	%	yes	'5'	'%'

+7	07/87	36/F0 36	36/F0 36	6	^	yes	'6'	'^'

+8	08/88	3D/F0 3D	3D/F0 3D	7	&	yes	'7'	'&'

+9	09/89	3E/F0 3E	3E/F0 3E	8	*	yes	'8'	'*'

+10	0A/8A	46/F0 46	46/F0 46	9	(	yes	'9'	'('

+11	0B/8B	45/F0 45	45/F0 45	0	)	yes	'0'	')'

+12	0C/8C	4E/F0 4E	4E/F0 4E	-	_	yes	'-'	'_'

+13	0D/8D	55/F0 55	55/F0 55	=	+	yes	'='	'+'

+15	0E/8E	66/F0 66	66/F0 66	Backspace		no	KBD_BACKSPACE	NULL

+16	0F/8F	0D/F0 0D	0D/F0 0D	Tab		no	KBD_TAB	NULL

+17	10/90	15/F0 15	15/F0 15	q	Q	yes	'q'	'Q'

+18	11/91	1D/F0 1D	1D/F0 1D	w	W	yes	'w'	'W'

+19	12/92	24/F0 24	24/F0 24	e	E	yes	'e'	'E'

+20	13/93	2D/F0 2D	2D/F0 2D	r	R	yes	'r'	'R'

+21	14/94	2C/F0 2C	2C/F0 2C	t	T	yes	't'	'T'

+22	15/95	35/F0 35	35/F0 35	y	Y	yes	'y'	'Y'

+23	16/96	3C/F0 3C	3C/F0 3C	u	U	yes	'u'	'U'

+24	17/97	43/F0 43	43/F0 43	i	I	yes	'i'	'I'

+25	18/98	44/F0 44	44/F0 44	o	O	yes	'o'	'O'

+26	19/99	4D/F0 4D	4D/F0 4D	p	P	yes	'p'	'P'

+27	1A/9A	54/F0 54	54/F0 54	[	{	yes	'['	'{'

+28	1B/9B	5B/F0 5B	5B/F0 5B	]	}	yes	']'	'}'

+30	3A/BA	58/F0 58	58/F0 58	Caps Lock		no	KBD_CAPS_LOCK	NULL

+31	1E/9E	1C/F0 1C	1C/F0 1C	a	A	yes	'a'	'A'

+32	1F/9F	1B/F0 1B	1B/F0 1B	s	S	yes	's'	'S'

+33	20/A0	23/F0 23	23/F0 23	d	D	yes	'd'	'D'

+34	21/A1	2B/F0 2B	2B/F0 2B	f	F	yes	'f'	'F'

+35	22/A2	34/F0 34	34/F0 34	g	G	yes	'g'	'G'

+36	23/A3	33/F0 33	33/F0 33	h	H	yes	'h'	'H'

+37	24/A4	3B/F0 3B	3B/F0 3B	j	J	yes	'j'	'J'

+38	25/A5	42/F0 42	42/F0 42	k	K	yes	'k'	'K'

+39	26/A6	4B/F0 4B	4B/F0 4B	l	L	yes	'l'	'L'

+40	27/A7	4C/F0 4C	4C/F0 4C	;	:	yes	';'	':'

+41	28/A8	52/F0 52	52/F0 52	'	"	yes	'\''	'"'

+43	1C/9C	5A/F0 5A	5A/F0 5A	Enter	Enter	no	KBD_ENTER	NULL

+44	2A/AA	12/F0 12	12/F0 12	Left Shift		no	KBD_LEFT_SHIFT	NULL

+46	2C/AC	1A/F0 1A	1A/F0 1A	z	Z	yes	'z'	'Z'

+47	2D/AD	22/F0 22	22/F0 22	x	X	yes	'x'	'X'

+48	2E/AE	21/F0 21	21/F0 21	c	C	yes	'c'	'C'

+49	2F/AF	2A/F0 2A	2A/F0 2A	v	V	yes	'v'	'V'

+50	30/B0	32/F0 32	32/F0 32	b	B	yes	'b'	'B'

+51	31/B1	31/F0 31	31/F0 31	n	N	yes	'n'	'N'

+52	32/B2	3A/F0 3A	3A/F0 3A	m	M	yes	'm'	'M'

+53	33/B3	41/F0 41	41/F0 41	,	<	yes	','	'<'

+54	34/B4	49/F0 49	49/F0 49	.	>	yes	'.'	'>'

+55	35/B5	4A/F0 4A	4A/F0 4A	/	?	yes	'/'	'?'

+57	36/B6	59/F0 59	59/F0 59	Right Shift		no	KBD_RIGHT_SHIFT	NULL

+58	1D/9D	14/F0 14	11/F0 11	Left Ctrl		no	KBD_LEFT_CTRL	NULL

+60	38/B8	11/F0 11	19/F0 19	Left Alt		no	KBD_LEFT_ALT	NULL

+61	39/B9	29/F0 29	29/F0 29	Spacebar		no	KBD_SPACEBAR	NULL

+62	E0 38/E0 B8	E0 11/E0 F0 11	39/F0 39	Right Alt		no	KBD_RIGHT_ALT	NULL

+64	E0 1D/E0 9D	E0 14/E0 F0 14	58/F0 58	Right Ctrl		no	KBD_RIGHT_CTRL	NULL

+75	E0 52/E0 D2 (base)	E0 70/E0 F0 70 (base)	67/F0 67	Insert		no	KBD_INSERT	NULL

+76	E0 4B/E0 CB (base)	E0 71/E0 F0 71 (base)	64/F0 64	Delete		no	KBD_DELETE	NULL

+79	E0 4B/E0 CB (base)	E0 6B/E0 F0 6B (base)	61/F0 61	Left Arrow		no	KBD_LEFT_ARROW	NULL

+80	E0 47/E0 C7 (base)	E0 6C/E0 F0 6C (base)	6E/F0 6E	Home		no	KBD_HOME	NULL

+81	E0 4F/E0 CF (base)	E0 69/E0 F0 69 (base)	65/F0 65	End		no	KBD_END	NULL

+83	E0 48/E0 C8 (base)	E0 75/E0 F0 75 (base)	63/F0 63	Up Arrow		no	KBD_UP_ARROW	NULL

+84	E0 50/E0 D0 (base)	E0 72/E0 F0 72 (base)	60/F0 60	Down Arrow		no	KBD_DOWN_ARROW	NULL

+85	E0 49/E0 C9 (base)	E0 7D/E0 F0 7D (base)	6F/F0 6F	Page Up		no	KBD_PAGE_UP	NULL

+86	E0 51/E0 D1 (base)	E0 7A/E0 F0 7A (base)	6D/F0 6D	Page Down		no	KBD_PAGE_DOWN	NULL

+89	E0 4D/E0 CD (base)	E0 74/E0 F0 74 (base)	6A/F0 6A	Right Arrow		no	KBD_RIGHT_ARROW	NULL

+90	45/C5	77/F0 77	76/F0 76	Num Lock		no	KBD_NUM_LOCK	NULL

+91	47/C7	6C/F0 6C	6C/F0 6C	Keypad 7		no	KBD_KEYPAD_7	NULL

+92	4B/CB	6B/F0 6B	6B/F0 6B	Keypad 4		no	KBD_KEYPAD_4	NULL

+93	4F/CF	69/F0 69	69/F0 69	Keypad 1		no	KBD_KEYPAD_1	NULL

+95	E0 35/E0 B5 (base)	E0 4A/E0 F0 4A (base)	77/F0 77	Keypad /		no	KBD_KEYPAD_/	NULL

+96	48/C8	75/F0 75	75/F0 75	Keypad 8		no	KBD_KEYPAD_8	NULL

+97	4C/CC	73/F0 73	73/F0 73	Keypad 5		no	KBD_KEYPAD_5	NULL

+98	50/D0	72/F0 72	72/F0 72	Keypad 2		no	KBD_KEYPAD_2	NULL

+99	52/D2	70/F0 70	70/F0 70	Keypad 0		no	KBD_KEYPAD_0	NULL

+100	37/B7	7C/F0 7C	7E/F0 7E	Keypad *		no	KBD_KEYPAD_*	NULL

+101	49/C9	7D/F0 7D	7D/F0 7D	Keypad 9		no	KBD_KEYPAD_9	NULL

+102	4D/CD	74/F0 74	74/F0 74	Keypad 6		no	KBD_KEYPAD_6	NULL

+103	51/D1	7A/F0 7A	7A/F0 7A	Keypad 3		no	KBD_KEYPAD_3	NULL

+104	53/D3	71/F0 71	71/F0 71	Keypad .		no	KBD_KEYPAD_.	NULL

+105	4A/CA	7B/F0 7B	84/F0 84	Keypad -		no	KBD_KEYPAD_-	NULL

+106	4E/CE	79/F0 79	7C/F0 7C	Keypad +		no	KBD_KEYPAD_+	NULL

+108	E0 1C/E0 9C	E0 5A/E0 F0 5A	79/F0 79	Keypad Enter		no	KBD_KEYPAD_ENTER	NULL

+110	01/81	76/F0 76	08/F0 08	Esc		no	KBD_ESC	NULL

+112	3B/BB	05/F0 05	07/F0 07	F1		no	KBD_F1	NULL

+113	3C/BC	06/F0 06	0F/F0 0F	F2		no	KBD_F2	NULL

+114	3D/BD	04/F0 04	17/F0 17	F3		no	KBD_F3	NULL

+115	3E/BE	0C/F0 0C	1F/F0 1F	F4		no	KBD_F4	NULL

+116	3F/BF	03/F0 03	27/F0 27	F5		no	KBD_F5	NULL

+117	40/C0	0B/F0 0B	2F/F0 2F	F6		no	KBD_F6	NULL

+118	41/C1	83/F0 83	37/F0 37	F7		no	KBD_F7	NULL

+119	42/C2	0A/F0 0A	3F/F0 3F	F8		no	KBD_F8	NULL

+120	43/C3	01/F0 01	47/F0 47	F9		no	KBD_F9	NULL

+121	44/C4	09/F0 09	4F/F0 4F	F10		no	KBD_F10	NULL

+122	57/D7	78/F0 78	56/F0 56	F11		no	KBD_F11	NULL

+123	58/D8	07/F0 07	5E/F0 5E	F12		no	KBD_F12	NULL

+124	E0 2A E0 37/E0 B7 E0 AA	E0 12 E0 7C/E0 F0 7C E0 F0 12	57/F0 57	Print Screen		no	KBD_PRINT_SCREEN	NULL

+125	46/C6	7E/F0 7E	5F/F0 5F	Scroll Lock		no	KBD_SCROLL_LOCK	NULL

+126	E1 1D 45/E1 9D C5	E1 14 77 E1/F0 14 F0 77	62/F0 62	Pause Break		no	KBD_PAUSE_BREAK	NULL

+29	2B/AB	5D/F0 5D	5C/F0 5C	\	|	yes	'\\'	'|'
\ No newline at end of file
diff --git a/src/kernel/srcmap.txt b/src/kernel/srcmap.txt
new file mode 100644
index 0000000..c9219a7
--- /dev/null
+++ b/src/kernel/srcmap.txt
@@ -0,0 +1,17 @@
++------------+
+| Source map |
++------------+
+
+
+Source File          Description
+----------------------------------------------------------------
+io.c                 low level memory management stuff, io utils
+log.c                logging, panics, assert, etc
+vga.c                vga drivers
+timer.c              sets up interrupt timer
+descriptor_tables.c  sets up IDT and GDT
+paging.c             sets up paging
+pic.c                IRQ handler
+interrupts.c         ISR handler
+boot.s               calls kmain
+main.c               entry point
diff --git a/src/kernel/stdarg.h b/src/kernel/stdarg.h
new file mode 100644
index 0000000..ce9bbc4
--- /dev/null
+++ b/src/kernel/stdarg.h
@@ -0,0 +1,130 @@
+/* Copyright (C) 1989-2020 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+/*
+ * ISO C Standard:  7.15  Variable arguments  <stdarg.h>
+ */
+
+#ifndef _STDARG_H
+#ifndef _ANSI_STDARG_H_
+#ifndef __need___va_list
+#define _STDARG_H
+#define _ANSI_STDARG_H_
+#endif /* not __need___va_list */
+#undef __need___va_list
+
+/* Define __gnuc_va_list.  */
+
+#ifndef __GNUC_VA_LIST
+#define __GNUC_VA_LIST
+typedef __builtin_va_list __gnuc_va_list;
+#endif
+
+/* Define the standard macros for the user,
+   if this invocation was from the user program.  */
+#ifdef _STDARG_H
+
+#define va_start(v, l) __builtin_va_start(v, l)
+#define va_end(v) __builtin_va_end(v)
+#define va_arg(v, l) __builtin_va_arg(v, l)
+#if !defined(__STRICT_ANSI__) || __STDC_VERSION__ + 0 >= 199900L ||            \
+	__cplusplus + 0 >= 201103L
+#define va_copy(d, s) __builtin_va_copy(d, s)
+#endif
+#define __va_copy(d, s) __builtin_va_copy(d, s)
+
+/* Define va_list, if desired, from __gnuc_va_list. */
+/* We deliberately do not define va_list when called from
+   stdio.h, because ANSI C says that stdio.h is not supposed to define
+   va_list.  stdio.h needs to have access to that data type,
+   but must not use that name.  It should use the name __gnuc_va_list,
+   which is safe because it is reserved for the implementation.  */
+
+#ifdef _BSD_VA_LIST
+#undef _BSD_VA_LIST
+#endif
+
+#if defined(__svr4__) || (defined(_SCO_DS) && !defined(__VA_LIST))
+/* SVR4.2 uses _VA_LIST for an internal alias for va_list,
+   so we must avoid testing it and setting it here.
+   SVR4 uses _VA_LIST as a flag in stdarg.h, but we should
+   have no conflict with that.  */
+#ifndef _VA_LIST_
+#define _VA_LIST_
+#ifdef __i860__
+#ifndef _VA_LIST
+#define _VA_LIST va_list
+#endif
+#endif /* __i860__ */
+typedef __gnuc_va_list va_list;
+#ifdef _SCO_DS
+#define __VA_LIST
+#endif
+#endif /* _VA_LIST_ */
+#else  /* not __svr4__ || _SCO_DS */
+
+/* The macro _VA_LIST_ is the same thing used by this file in Ultrix.
+   But on BSD NET2 we must not test or define or undef it.
+   (Note that the comments in NET 2's ansi.h
+   are incorrect for _VA_LIST_--see stdio.h!)  */
+#if !defined(_VA_LIST_) || defined(__BSD_NET2__) || defined(____386BSD____) || \
+	defined(__bsdi__) || defined(__sequent__) || defined(__FreeBSD__) ||       \
+	defined(WINNT)
+/* The macro _VA_LIST_DEFINED is used in Windows NT 3.5  */
+#ifndef _VA_LIST_DEFINED
+/* The macro _VA_LIST is used in SCO Unix 3.2.  */
+#ifndef _VA_LIST
+/* The macro _VA_LIST_T_H is used in the Bull dpx2  */
+#ifndef _VA_LIST_T_H
+/* The macro __va_list__ is used by BeOS.  */
+#ifndef __va_list__
+typedef __gnuc_va_list va_list;
+#endif /* not __va_list__ */
+#endif /* not _VA_LIST_T_H */
+#endif /* not _VA_LIST */
+#endif /* not _VA_LIST_DEFINED */
+#if !(defined(__BSD_NET2__) || defined(____386BSD____) || defined(__bsdi__) || \
+	  defined(__sequent__) || defined(__FreeBSD__))
+#define _VA_LIST_
+#endif
+#ifndef _VA_LIST
+#define _VA_LIST
+#endif
+#ifndef _VA_LIST_DEFINED
+#define _VA_LIST_DEFINED
+#endif
+#ifndef _VA_LIST_T_H
+#define _VA_LIST_T_H
+#endif
+#ifndef __va_list__
+#define __va_list__
+#endif
+
+#endif /* not _VA_LIST_, except on certain systems */
+
+#endif /* not __svr4__ */
+
+#endif /* _STDARG_H */
+
+#endif /* not _ANSI_STDARG_H_ */
+#endif /* not _STDARG_H */
diff --git a/src/kernel/switch_table.s b/src/kernel/switch_table.s
new file mode 100644
index 0000000..d04629b
--- /dev/null
+++ b/src/kernel/switch_table.s
@@ -0,0 +1,25 @@
+	[section .text]
+	[bits 32]
+
+;;; TODO: remove useless frame pointer stuff
+	
+	[global load_page_directory]
+load_page_directory:
+	push ebp 					; Save frame pointer
+	mov ebp, esp
+	mov eax, [esp + 8]			; Pointer to directory
+	mov cr3, eax
+	mov esp, ebp
+	pop ebp
+	ret
+
+	[global enable_paging]
+enable_paging:
+	push ebp
+	mov ebp, esp
+	mov eax, cr0
+	or eax, 0x80000000
+	mov cr0, eax
+	mov esp, ebp
+	pop ebp
+	ret
diff --git a/src/kernel/timer.c b/src/kernel/timer.c
new file mode 100644
index 0000000..145f8f8
--- /dev/null
+++ b/src/kernel/timer.c
@@ -0,0 +1,32 @@
+#include "timer.h"
+#include "io.h"
+#include "log.h"
+#include "pic.h"
+#include "registers.h"
+
+static ulong tick = 0;
+
+static void timer_cb(struct registers *regs)
+{
+	// do nothing :)
+}
+
+void init_timer(uint hz)
+{
+	add_interrupt_handler(IRQ_TO_INT(0), timer_cb);
+
+	uint divisor = TIMER_FREQ / hz;
+
+	kprintf("Divisor is %d\n", divisor);
+
+	outb(0x43, 0x36);
+	io_wait();
+	uchar l = divisor & 0xff, h = (divisor >> 8) & 0xff;
+
+	outb(0x40, l);
+	io_wait();
+	outb(0x40, h);
+	io_wait();
+
+	kprintf("Initialized timer\n");
+}
diff --git a/src/kernel/timer.h b/src/kernel/timer.h
new file mode 100644
index 0000000..50297c4
--- /dev/null
+++ b/src/kernel/timer.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "kint.h"
+
+#define TIMER_FREQ 1193180
+
+void init_timer(uint hz);
diff --git a/src/kernel/vga.c b/src/kernel/vga.c
new file mode 100644
index 0000000..bfd13e0
--- /dev/null
+++ b/src/kernel/vga.c
@@ -0,0 +1,133 @@
+#include "vga.h"
+#include "io.h"
+#include "log.h"
+
+static uint cursor_x = 0;
+static uint cursor_y = 0;
+
+static ushort color = WHITE;
+
+static ushort *fb = (ushort *)0xB8000;
+
+static void move_cursor()
+{
+	ushort loc = cursor_y * 80 + cursor_x;
+	outb(0x3d4, 14); // Setting high cursor byte
+	outb(0x3d4, loc >> 8);
+
+	outb(0x3d4, 15); // low byte
+	outb(0x3d4, loc & 0xff);
+}
+
+static void scroll()
+{
+	ushort blank = ' ' | color << 8;
+
+	while (cursor_y >= 25) // end of line
+	{
+		// scroll everything up
+		memcpy(fb, &fb[80], 24 * 80 * 2);
+
+		for (int i = 24 * 80; i < 25 * 80; i++)
+		{
+			fb[i] = blank;
+		}
+
+		cursor_y--;
+	}
+}
+
+void vga_set_color(enum vga_colors fg, enum vga_colors bg)
+{
+	color = (bg << 4) | fg & 0xf;
+}
+
+void vga_put(char c)
+{
+	switch (c)
+	{
+	case '\b':
+		if (cursor_x > 0)
+			cursor_x--;
+		fb[cursor_y * 80 + cursor_x] = ' ' | (color << 8);
+		break;
+	case '\t':
+		cursor_x = (cursor_x + 8) & ~7;
+		break;
+	case '\n':
+		cursor_y++;
+	case '\r':
+		cursor_x = 0;
+		break;
+	default:
+		fb[cursor_y * 80 + cursor_x] = c | (color << 8);
+		cursor_x++;
+	}
+
+	if (cursor_x >= 80) // off screen
+	{
+		cursor_x = 0;
+		cursor_y++;
+	}
+
+	scroll();
+	move_cursor();
+}
+
+void vga_clear()
+{
+	memset(fb, 0, 80 * 25 * 2);
+	cursor_x = 0;
+	cursor_y = 0;
+	move_cursor();
+}
+
+void vga_write(char *c)
+{
+	for (int i = 0; c[i]; i++)
+		vga_put(c[i]);
+}
+
+void vga_putd(uint d)
+{
+	char str[48];
+	memset(str, 0, 48);
+	uint i = 0;
+
+	do
+	{
+		str[i++] = (d % 10) + '0';
+		d /= 10;
+	} while (d > 0 && i < 48); // should never be more than 48 digits anyway
+
+	for (uint j = i; j; j--)
+	{
+		vga_put(str[j - 1]);
+	}
+}
+
+static bool vga_put_nibble(uchar n, bool first)
+{
+//	if (first && n == 0)
+//		return true;
+
+	if (n <= 9)
+		vga_put('0' + n);
+	else
+		vga_put('A' + n - 10);
+
+	return false;
+}
+
+void vga_putx(uint x)
+{
+	bool first = false;
+
+	for (int shift = 24; shift >= 0; shift -= 8)
+	{
+		uchar byte = x >> shift;
+
+		first = vga_put_nibble((byte & 0xf0) >> 4, first);
+		first = vga_put_nibble(byte & 0x0f, first);
+	}
+}
diff --git a/src/kernel/vga.h b/src/kernel/vga.h
new file mode 100644
index 0000000..9c7ba16
--- /dev/null
+++ b/src/kernel/vga.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "kint.h"
+
+enum vga_colors
+{
+	BLACK = 0,
+	BLUE,
+	GREEN,
+	CYAN,
+	RED,
+	MAGENTA,
+	BROWN,
+	LIGHT_GREY,
+	DARK_GREY,
+	LIGHT_BLUE,
+	LIGHT_GREEN,
+	LIGHT_CYAN,
+	LIGHT_RED,
+	LIGHT_MAGENTA,
+	LIGHT_BROWN,
+	WHITE,
+};
+
+void vga_set_color(enum vga_colors fg, enum vga_colors bg);
+void vga_put(char c);
+void vga_putd(uint d);
+void vga_putx(uint x);
+void vga_clear();
+void vga_write(char *c);