summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLizzy Fleckenstein <lizzy@vlhl.dev>2023-12-19 01:54:39 +0100
committerLizzy Fleckenstein <lizzy@vlhl.dev>2023-12-19 02:11:32 +0100
commit6d263c7d4e0f4b1d34694b5d3d159ccb20b3db02 (patch)
tree41578268cf68b2d9ea1737687a0f98af979948d8
parent5881b4d5c1040c762599f90e091e4cc4c3abe6b1 (diff)
downloadcuddles-6d263c7d4e0f4b1d34694b5d3d159ccb20b3db02.tar.xz
keyboard driver and threads
* PS/2 keyboard driver * interactive shell * move away from \0 terminated strings to sized slices * coroutine threads and IRQ queues
-rw-r--r--Makefile8
-rw-r--r--fs/init9
-rw-r--r--fs/keymap/debin0 -> 256 bytes
-rw-r--r--fs/keymap/usbin0 -> 256 bytes
-rwxr-xr-xmkkeymap.sh5
-rw-r--r--stage3/ata.c34
-rw-r--r--stage3/def.h8
-rw-r--r--stage3/font.c42
-rw-r--r--stage3/font.h9
-rw-r--r--stage3/fs.c13
-rw-r--r--stage3/fs.h7
-rw-r--r--stage3/gfx.c2
-rw-r--r--stage3/gfx.h2
-rw-r--r--stage3/halt.c9
-rw-r--r--stage3/halt.h6
-rw-r--r--stage3/heap.c14
-rw-r--r--stage3/interrupts.c143
-rw-r--r--stage3/interrupts.h2
-rw-r--r--stage3/io.h5
-rw-r--r--stage3/isr.lua2
-rw-r--r--stage3/main.c183
-rw-r--r--stage3/pci.c10
-rw-r--r--stage3/pic.c5
-rw-r--r--stage3/pic.h1
-rw-r--r--stage3/ps2.c205
-rw-r--r--stage3/ps2.h6
-rw-r--r--stage3/shell.c172
-rw-r--r--stage3/shell.h9
-rw-r--r--stage3/string.c74
-rw-r--r--stage3/string.h9
-rw-r--r--stage3/thread.c82
-rw-r--r--stage3/thread.h40
-rw-r--r--stage3/yield.asm45
-rw-r--r--util/dump2cuddlekeys/.gitignore1
-rw-r--r--util/dump2cuddlekeys/Makefile8
-rw-r--r--util/dump2cuddlekeys/main.c89
36 files changed, 936 insertions, 323 deletions
diff --git a/Makefile b/Makefile
index e02f94c..cf4bd3d 100644
--- a/Makefile
+++ b/Makefile
@@ -28,7 +28,11 @@ STAGE3 = \
stage3/ata.o \
stage3/string.o \
stage3/pci.o \
- stage3/fs.o
+ stage3/fs.o \
+ stage3/yield.o \
+ stage3/ps2.o \
+ stage3/thread.o \
+ stage3/shell.o
PAD_BOUNDARY = pad() { truncate -s $$(echo "($$(du -b $$1 | cut -f1)+$$2-1)/$$2*$$2" | bc) $$1; }; pad
@@ -52,7 +56,7 @@ stage3/%.o: stage3/%.asm
nasm -f elf64 $< -o $@
stage3/%.o: stage3/%.c
- cc $(CFLAGS) -c $< -o $@
+ gcc $(CFLAGS) -c $< -o $@
stage3/isr.asm: stage3/isr.lua
lua stage3/isr.lua > stage3/isr.asm
diff --git a/fs/init b/fs/init
index 61b0a77..6ff2326 100644
--- a/fs/init
+++ b/fs/init
@@ -1,10 +1,3 @@
font fonts/Bm437_IBM_VGA_8x16.cuddlefont
+loadkeys keymap/de
echo hello world
-cat uwu.txt
-charset_demo
-lspci
-echo meow meow meow
-img blahaj.cuddleimg
-font fonts/ter-u16n.cuddlefont
-echo This is the terminus font!
-charset_demo
diff --git a/fs/keymap/de b/fs/keymap/de
new file mode 100644
index 0000000..bf81c13
--- /dev/null
+++ b/fs/keymap/de
Binary files differ
diff --git a/fs/keymap/us b/fs/keymap/us
new file mode 100644
index 0000000..035cd7f
--- /dev/null
+++ b/fs/keymap/us
Binary files differ
diff --git a/mkkeymap.sh b/mkkeymap.sh
new file mode 100755
index 0000000..953d1fc
--- /dev/null
+++ b/mkkeymap.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+set -e
+make -qC util/dump2cuddlekeys
+doas loadkeys -C/dev/tty1 "$1"
+dumpkeys -C/dev/tty1 | util/dump2cuddlekeys/dump2cuddlekeys > "fs/keymap/$1"
diff --git a/stage3/ata.c b/stage3/ata.c
index ccfd695..6eb4c9c 100644
--- a/stage3/ata.c
+++ b/stage3/ata.c
@@ -34,7 +34,7 @@ typedef struct __attribute__((packed)) {
typedef struct {
unsigned int bits : 4;
- bool doll : 1;
+ bool doll : 1;
bool one0 : 1;
bool lba : 1;
bool one1 : 1;
@@ -48,22 +48,22 @@ void ata_recv(u16 *buffer)
if (status.err) {
u8 err = inb(IO_ATA0_DATA + ATA_IO_ERR);
- const char *errors[] = {
- "address mark not found\n",
- "track zero not found\n",
- "aborted command\n",
- "media change request\n",
- "id not found\n",
- "media changed\n",
- "uncorrectable data error\n",
- "bad block detected\n",
+ str errors[] = {
+ S("address mark not found\n"),
+ S("track zero not found\n"),
+ S("aborted command\n"),
+ S("media change request\n"),
+ S("id not found\n"),
+ S("media changed\n"),
+ S("uncorrectable data error\n"),
+ S("bad block detected\n"),
};
for (int i = 0; i < 8; i++)
if (err & (1 << i))
print(errors[i]);
-
- panic("ata0-witch error\n");
+
+ panic(S("ata0-witch error\n"));
} else if (status.drq)
break;
}
@@ -75,14 +75,14 @@ void ata_recv(u16 *buffer)
void ata_delay()
{
for (int i = 0; i < 15; i++)
- inb(IO_ATA0_DATA + ATA_IO_STATUS);
+ inb(IO_ATA0_DATA + ATA_IO_STATUS);
}
void ata_init()
{
u8 floating = inb(IO_ATA0_DATA + ATA_IO_STATUS);
if (floating == 0xFF)
- panic("ata0 floating\n");
+ panic(S("ata0 floating\n"));
outb(IO_ATA0_DATA + ATA_IO_HEAD, BITCAST(((ata_head) {
.bits = 0,
@@ -99,19 +99,19 @@ void ata_init()
u8 status_byte = inb(IO_ATA0_DATA + ATA_IO_STATUS);
if (status_byte == 0)
- panic("no ata0-witch drive\n");
+ panic(S("no ata0-witch drive\n"));
while (BITCAST(status_byte, u8, ata_status).bsy)
status_byte = inb(IO_ATA0_DATA + ATA_IO_STATUS);
if (inb(IO_ATA0_DATA + ATA_IO_LBA_MID) != 0 || inb(IO_ATA0_DATA + ATA_IO_LBA_HIGH) != 0)
- panic("ata0-witch is not ATA\n");
+ panic(S("ata0-witch is not ATA\n"));
u16 *idvec = malloc(256 * sizeof *idvec);
ata_recv(idvec);
if (!(idvec[83] & (1 << 10)))
- panic("ata0-witch does not support LBA48 mode\n");
+ panic(S("ata0-witch does not support LBA48 mode\n"));
// u64 lba48_sectors = *(u64 *) &idvec[100];
// print_num(lba48_sectors, 10, 0); print("\n");
diff --git a/stage3/def.h b/stage3/def.h
index e973d5e..1da628e 100644
--- a/stage3/def.h
+++ b/stage3/def.h
@@ -24,4 +24,12 @@ typedef u8 bool;
#define BITCAST(expr, from, to) (((union { from f; to t; }) { .f = expr }).t)
+typedef struct {
+ usize len;
+ char *data;
+} str;
+
+#define S(x) ((str) { sizeof (x) - 1, (x) })
+#define NILS ((str) { 0, nil })
+
#endif
diff --git a/stage3/font.c b/stage3/font.c
index 9e315e6..69017ca 100644
--- a/stage3/font.c
+++ b/stage3/font.c
@@ -142,37 +142,41 @@ void print_char(char c)
fix_cursor();
}
-void print(const char *line)
+void print(str line)
{
- while (*line != '\0')
- print_char(*line++);
+ for (usize i = 0; i < line.len; i++)
+ print_char(line.data[i]);
}
-void printn(const char *line, usize len)
+void print_num_pad(u64 x, u8 base, u8 pad_len, char pad_char)
{
- for (usize i = 0; i < len; i++)
- print_char(*line++);
-}
-
-void print_padded(u64 x, u8 base, u8 pad_len, char pad_char)
-{
- char digit[65];
- char *ptr = &digit[64];
- *ptr = '\0';
+ char buffer[64];
+ usize idx = 64;
do {
u8 digit = x % base;
- *--ptr = digit + (digit < 10 ? '0' : ('A' - 10));
+ buffer[--idx] = digit + (digit < 10 ? '0' : ('A' - 10));
x /= base;
} while (x != 0);
- while (ptr > digit + 64 - pad_len)
- *--ptr = pad_char;
+ while (idx > (usize) (64 - pad_len))
+ buffer[--idx] = pad_char;
+
+ print((str) { 64 - idx, &buffer[idx] });
+}
+
+void print_num(u64 x, u8 base)
+{
+ print_num_pad(x, base, 0, ' ');
+}
- print(ptr);
+void print_dec(u64 x)
+{
+ print_num(x, 10);
}
-void print_num(u64 x, u8 base, u8 pad)
+void print_hex(u64 x)
{
- print_padded(x, base, pad, ' ');
+ print_num(x, 16);
}
+
diff --git a/stage3/font.h b/stage3/font.h
index ec99118..400fb3d 100644
--- a/stage3/font.h
+++ b/stage3/font.h
@@ -8,10 +8,11 @@ void font_set_size(u16 size);
void font_load_blob(const void *blob);
void font_load_classic();
-void print(const char *line);
-void printn(const char *line, usize len);
+void print(str line);
void print_char(char c);
-void print_num(u64 x, u8 base, u8 pad_len);
-void print_padded(u64 x, u8 base, u8 pad_len, char pad_char);
+void print_dec(u64 x);
+void print_hex(u64 x);
+void print_num(u64 x, u8 base);
+void print_num_pad(u64 x, u8 base, u8 pad_len, char pad_char);
#endif
diff --git a/stage3/fs.c b/stage3/fs.c
index 9f7ea51..d4a325f 100644
--- a/stage3/fs.c
+++ b/stage3/fs.c
@@ -4,7 +4,7 @@
#include "memory.h"
#include "heap.h"
-file fs_read(const char *filename)
+str fs_read(str filename)
{
u64 start = (*(u32 *) (0x1000-10-8))/512;
@@ -13,16 +13,17 @@ file fs_read(const char *filename)
if (memcmp(info+257, "ustar", 5) != 0) {
free(info);
- return (file) { .len = 0, .data = nil };
+ return NILS;
}
- u8 *infop = info+124;
- usize fsize = parse_num(&infop, 8, 11);
+ usize fsize;
+ str_parse_num((str) { 11, (char *) info+124 }, 8, &fsize);
+
usize fsect = (fsize+511)/512;
- if (memcmp(info, filename, strlen(filename) + 1) == 0) {
+ if (memcmp(info, filename.data, filename.len) == 0 && info[filename.len] == '\0') {
free(info);
- return (file) { .len = fsize, .data = ata_read_full(start+1, fsect) };
+ return (str) { .len = fsize, .data = ata_read_full(start+1, fsect) };
} else {
free(info);
start += 1 + fsect;
diff --git a/stage3/fs.h b/stage3/fs.h
index b585577..c1fea24 100644
--- a/stage3/fs.h
+++ b/stage3/fs.h
@@ -3,11 +3,6 @@
#include "def.h"
-typedef struct {
- usize len;
- void *data;
-} file;
-
-file fs_read(const char *filename);
+str fs_read(str filename);
#endif
diff --git a/stage3/gfx.c b/stage3/gfx.c
index 79c76a3..b01e89a 100644
--- a/stage3/gfx.c
+++ b/stage3/gfx.c
@@ -32,7 +32,7 @@ void gfx_set_area(u16 x, u16 y, u16 w, u16 h, u32 col)
}
}
-void gfx_draw_img(u16 x, u16 y, u16 w, u16 h, color *img)
+void gfx_draw_img(u16 x, u16 y, u16 w, u16 h, u32 *img)
{
void *cbeg = (void *) (u64) (gfx_info->framebuffer + y * gfx_info->pitch + x * sizeof(color));
for (u16 yi = 0; yi < h; cbeg += gfx_info->pitch, yi++)
diff --git a/stage3/gfx.h b/stage3/gfx.h
index 6eee074..5d9c8b3 100644
--- a/stage3/gfx.h
+++ b/stage3/gfx.h
@@ -17,6 +17,6 @@ typedef struct __attribute__((packed)) {
u32 make_color(color col);
void gfx_set_pixel(u16 x, u16 y, u32 col);
void gfx_set_area(u16 x, u16 y, u16 w, u16 h, u32 col);
-void gfx_draw_img(u16 x, u16 y, u16 w, u16 h, color *img);
+void gfx_draw_img(u16 x, u16 y, u16 w, u16 h, u32 *img);
#endif
diff --git a/stage3/halt.c b/stage3/halt.c
index 9e0ecce..b53a4f2 100644
--- a/stage3/halt.c
+++ b/stage3/halt.c
@@ -1,14 +1,15 @@
#include "halt.h"
#include "font.h"
+#include "pic.h"
-void halt()
+void freeze()
{
for (;;)
- asm volatile("hlt");
+ wait_irq();
}
-void panic(char *msg)
+void panic(str msg)
{
print(msg);
- halt();
+ freeze();
}
diff --git a/stage3/halt.h b/stage3/halt.h
index 99b1717..6e7ca34 100644
--- a/stage3/halt.h
+++ b/stage3/halt.h
@@ -1,7 +1,9 @@
#ifndef HALT_H
#define HALT_H
-void halt();
-void panic(char *msg);
+#include "def.h"
+
+void freeze();
+void panic(str msg);
#endif
diff --git a/stage3/heap.c b/stage3/heap.c
index 0a82213..c9e7edb 100644
--- a/stage3/heap.c
+++ b/stage3/heap.c
@@ -18,7 +18,7 @@ void free(void *ptr)
Header *h = ((Header *) ptr) - 1;
if (h->next != MAGIC)
- panic("free: invalid pointer");
+ panic(S("free: invalid pointer"));
Header *next = free_ptr->next;
free_ptr->next = h;
@@ -28,7 +28,7 @@ void free(void *ptr)
static void defragment()
{
//usize num_blocks = 0;
- panic("defragment not implemented");
+ panic(S("defragment not implemented"));
}
void *try_malloc(usize size)
@@ -64,13 +64,13 @@ void *malloc(usize size)
{
void *p;
- p = try_malloc(size);
- if (p) return p;
- defragment();
+ // p = try_malloc(size);
+ // if (p) return p;
+ // defragment();
p = try_malloc(size);
if (p) return p;
- panic("out of memory");
+ panic(S("out of memory"));
return nil;
}
@@ -83,7 +83,7 @@ void *realloc(void *ptr, usize size)
Header *h = ((Header *) ptr) - 1;
if (h->next != MAGIC)
- panic("realloc: invalid pointer");
+ panic(S("realloc: invalid pointer"));
void *new = malloc(size);
diff --git a/stage3/interrupts.c b/stage3/interrupts.c
index 71f953f..4a4687c 100644
--- a/stage3/interrupts.c
+++ b/stage3/interrupts.c
@@ -3,102 +3,115 @@
#include "font.h"
#include "heap.h"
#include "pic.h"
+#include "thread.h"
+#include "io.h"
extern u64 idt_entries[256]; // isr.asm
-typedef struct {
- u64 which;
- u64 error_code;
- // automatically pushed
- u64 rip;
- u64 cs;
- u64 rflags;
- u64 rsp;
- u64 ss;
+typedef struct __attribute__((packed)) {
+ u64 rax, rdx, rcx, rsi, rdi, r8, r9, r10, r11;
+ u64 which, error_code;
+ u64 rip, cs, rflags, rsp, ss;
} interrupt_frame;
-static const char *exception[32] = {
- "Division Error",
- "Debug",
- "Non-maskable Interrupt",
- "Breakpoint",
- "Overflow",
- "Bound Range Exceeded",
- "Invalid Opcode",
- "Device Not Available",
- "Double Fault",
- "Coprocessor Segment Overrun",
- "Invalid TSS",
- "Segment Not Present",
- "Stack-Segment Fault",
- "General Protection Fault",
- "Page Fault",
- nil,
- "x87 Floating-Point Exception",
- "Alignment Check",
- "Machine Check",
- "SIMD Floating-Point Exception",
- "Virtualization Exception",
- "Control Protection Exception",
- nil, nil, nil, nil, nil, nil,
- "Hypervisor Injection Exception",
- "VMM Communication Exception",
- "Security Exception",
- nil,
+static str exception[32] = {
+ S("Division Error"),
+ S("Debug"),
+ S("Non-maskable Interrupt"),
+ S("Breakpoint"),
+ S("Overflow"),
+ S("Bound Range Exceeded"),
+ S("Invalid Opcode"),
+ S("Device Not Available"),
+ S("Double Fault"),
+ S("Coprocessor Segment Overrun"),
+ S("Invalid TSS"),
+ S("Segment Not Present"),
+ S("Stack-Segment Fault"),
+ S("General Protection Fault"),
+ S("Page Fault"),
+ NILS,
+ S("x87 Floating-Point Exception"),
+ S("Alignment Check"),
+ S("Machine Check"),
+ S("SIMD Floating-Point Exception"),
+ S("Virtualization Exception"),
+ S("Control Protection Exception"),
+ NILS, NILS, NILS, NILS, NILS, NILS,
+ S("Hypervisor Injection Exception"),
+ S("VMM Communication Exception"),
+ S("Security Exception"),
+ NILS,
};
static void dump_frame(interrupt_frame *frame)
{
- print("rip = "); print_num(frame->rip, 16, 0); print("\n");
- print("cs = "); print_num(frame->cs, 16, 0); print("\n");
- print("rflags = "); print_num(frame->rflags, 16, 0); print("\n");
- print("rsp = "); print_num(frame->rsp, 16, 0); print("\n");
- print("ss = "); print_num(frame->ss, 16, 0); print("\n");
+ print(S("rip = ")); print_hex(frame->rip); print_char('\n');
+ print(S("cs = ")); print_hex(frame->cs); print_char('\n');
+ print(S("rflags = ")); print_hex(frame->rflags); print_char('\n');
+ print(S("rsp = ")); print_hex(frame->rsp); print_char('\n');
+ print(S("ss = ")); print_hex(frame->ss); print_char('\n');
}
void interrupt_handler(interrupt_frame *frame)
{
if (frame->which < 32) {
- if (exception[frame->which] == nil) {
- print("Unknown Exception ");
- print_num(frame->which, 10, 0);
+ // TODO: possible race condition due to printing here
+ // when exception happens in printing code itself
+
+ if (exception[frame->which].data != nil) {
+ print(S("Unknown Exception "));
+ print_dec(frame->which);
} else {
print(exception[frame->which]);
}
- print("\n");
+ print_char('\n');
if (frame->which == 13) {
- const char *bits[8] = {
- "present",
- "write",
- "user",
- "reserved_write",
- "instruction_fetch",
- "protection_key",
- "shadow_stack",
- "software_guard_extensions",
+ str bits[8] = {
+ S("present"),
+ S("write"),
+ S("user"),
+ S("reserved_write"),
+ S("instruction_fetch"),
+ S("protection_key"),
+ S("shadow_stack"),
+ S("software_guard_extensions"),
};
u8 err = frame->error_code;
for (int i = 0; i < 8; i++) {
- print(bits[i]); print(" = "); print((err & 1) ? "true\n" : "false\n");
+ print(bits[i]); print(S(" = ")); print((err & 1) ? S("true\n") : S("false\n"));
err >>= 1;
}
} else {
- print("error_code = "); print_num(frame->error_code, 10, 0); print("\n");
+ print(S("error_code = ")); print_dec(frame->error_code); print_char('\n');
}
dump_frame(frame);
-
- halt();
+ freeze();
} else if (frame->which-32 < 16) {
- u64 irq = frame->which-32;
- print("IRQ "); print_num(irq, 10, 0); print("\n");
- ack_irq(irq);
+ if (queue_write.len == queue_write.cap) {
+ panic(S("queue exceeded\n"));
+ /*
+ // TODO: malloc would cause a race condition
+ queue_write.cap = queue_write.cap == 0 ? 1 : queue_write.cap * 2;
+ queue_write.data = realloc(queue_write.data, queue_write.cap);
+ */
+ }
+
+ event *e = &queue_write.data[queue_write.len++];
+ e->irq = frame->which-32;
+
+ if (e->irq == 1) {
+ e->data.scancode = inb(IO_PS2_DATA);
+ }
+
+ ack_irq(e->irq);
} else {
- print("Spurious Interrupt "); print_num(frame->which, 10, 0); print("\n");
- dump_frame(frame);
+ // print("Spurious Interrupt "); print_num(frame->which, 10, 0); print("\n");
+ // dump_frame(frame);
}
}
@@ -109,7 +122,7 @@ typedef struct {
idt_descriptor idtr;
-void init_interrupts()
+void interrupts_init()
{
typedef struct {
u16 offset_1;
diff --git a/stage3/interrupts.h b/stage3/interrupts.h
index 79d3488..333aca0 100644
--- a/stage3/interrupts.h
+++ b/stage3/interrupts.h
@@ -1,6 +1,6 @@
#ifndef INTERRUPTS_H
#define INTERRUPTS_H
-void init_interrupts();
+void interrupts_init();
#endif
diff --git a/stage3/io.h b/stage3/io.h
index 1265043..bca094d 100644
--- a/stage3/io.h
+++ b/stage3/io.h
@@ -39,11 +39,6 @@ static inline u32 inl(u16 port)
return val;
}
-static inline void hlt()
-{
- asm volatile("hlt");
-}
-
typedef enum {
IO_PIC1_CTRL = 0x20,
IO_PIC1_DATA = 0x21,
diff --git a/stage3/isr.lua b/stage3/isr.lua
index 6c81180..83515a5 100644
--- a/stage3/isr.lua
+++ b/stage3/isr.lua
@@ -36,7 +36,7 @@ isr_common:
push rax
cld
- lea rdi, [rsp+8*9]
+ mov rdi, rsp
call interrupt_handler
pop rax
diff --git a/stage3/main.c b/stage3/main.c
index 5362984..c8ff971 100644
--- a/stage3/main.c
+++ b/stage3/main.c
@@ -14,16 +14,40 @@ void init()
#include "interrupts.h"
#include "pic.h"
#include "io.h"
-#include "memory.h"
#include "ata.h"
-#include "fs.h"
-#include "string.h"
#include "pci.h"
+#include "ps2.h"
+#include "thread.h"
+#include "shell.h"
-void eat_whitespace(char **str)
+char keymap[256] = { '\0' };
+
+void keyboard_handler()
{
- while (**str == ' ' || **str == '\t')
- (*str)++;
+ str buffer = NILS;
+ usize cap = 0;
+ print(S("$ "));
+
+ for (;;) {
+ event *e = yield(nil);
+
+ char c = keymap[e->data.scancode];
+ if (c != '\0') {
+ print_char(c);
+
+ if (c == '\n') {
+ shell_run_cmd(buffer);
+ buffer.len = 0;
+ print(S("$ "));
+ } else {
+ if (buffer.len == cap)
+ buffer.data = realloc(buffer.data, cap = cap ? cap*2 : 1);
+ buffer.data[buffer.len++] = c;
+ }
+ }
+
+ free(e);
+ }
}
void kmain()
@@ -61,142 +85,31 @@ void kmain()
font_load_classic();
// memory map
- print("memory map:\n");
+ print(S("memory map:\n"));
for (usize i = 0; i < n_mreg; i++) {
- print_num((u64) mregs[i].start, 16, 16);
- print(" | ");
- print_num((u64) mregs[i].start + mregs[i].size, 16, 16);
- print(" | ");
- print_num(mregs[i].used, 10, 0);
- print("\n");
+ print_num_pad((u64) mregs[i].start, 16, 16, ' ');
+ print(S(" | "));
+ print_num_pad((u64) mregs[i].start + mregs[i].size, 16, 16, ' ');
+ print(S(" | "));
+ print_dec(mregs[i].used);
+ print(S("\n"));
}
- init_interrupts();
+ interrupts_init();
pic_init();
- ata_init();
-
- file f = fs_read("init");
- if (f.data == nil)
- panic("no init script");
-
- // this is horrible horrible horrible
- // if you read this i am genuinely sorry
-
- char *init = malloc(f.len+1);
- char *init_orig = init;
- init[f.len] = '\0';
- memcpy(init, f.data, f.len);
- free(f.data);
-
- for (;;) {
- eat_whitespace(&init);
+ thread_init();
- if (*init == '\0')
- break;
- else if (*init == '\n') {
- init++;
- continue;
- }
-
- if (strncmp(init, "echo ", strlen("echo ")) == 0) {
- init += strlen("echo ");
- usize idx = find_char(init, '\n');
- printn(init, idx);
- print("\n");
- init += idx;
- } else if (strncmp(init, "cat ", strlen("cat ")) == 0) {
- init += strlen("cat ");
- usize idx = find_char(init, '\n');
-
- char filename[idx+1];
- filename[idx] = '\0';
- memcpy(filename, init, idx);
-
- file f = fs_read(filename);
- if (f.data == nil) {
- print("cat: file not found: ");
- print(filename);
- print("\n");
- } else {
- printn(f.data, f.len);
- free(f.data);
- }
-
- init += idx;
- } else if (strncmp(init, "font ", strlen("font ")) == 0) {
- init += strlen("font ");
- usize idx = find_char(init, '\n');
-
- char filename[idx+1];
- filename[idx] = '\0';
- memcpy(filename, init, idx);
-
- file f = fs_read(filename);
- if (f.data == nil) {
- print("font: file not found: ");
- print(filename);
- print("\n");
- } else {
- if (f.len == 16*256)
- font_load_blob(f.data);
- else
- print("font: file has wrong size\n");
- free(f.data);
- }
+ ata_init();
+ ps2_init();
- init += idx;
- } else if (strncmp(init, "img ", strlen("img ")) == 0) {
- init += strlen("img ");
- usize idx = find_char(init, '\n');
+ shell_run_file(S("init"));
- char filename[idx+1];
- filename[idx] = '\0';
- memcpy(filename, init, idx);
+ thread *keyboard_thread = thread_create(S("keyboard"), &keyboard_handler);
+ irq_services[1] = keyboard_thread;
- file f = fs_read(filename);
- if (f.data == nil) {
- print("img: file not found: ");
- print(filename);
- print("\n");
- } else {
- if (f.len < 2 * sizeof(u32))
- print("img: missing header\n");
- else {
- u32 width = ((u32 *) f.data)[0];
- u32 height = ((u32 *) f.data)[1];
- if (f.len != 2 * sizeof(u32) + width * height * sizeof(color))
- panic("img: invalid file size\n");
- gfx_draw_img(gfx_info->width-width, 0, width, height, f.data + 2 * sizeof(u32));
- }
- free(f.data);
- }
-
- init += idx;
- } else if (strncmp(init, "lspci", strlen("lspci")) == 0) {
- pci_enumerate();
- init += find_char(init, '\n');
- } else if (strncmp(init, "charset_demo", strlen("charset_demo")) == 0) {
- const u8 max = '~' - '!' + 1;
-
- char str[max + 1];
- str[max] = '\0';
-
- for (u8 i = 0; i < max; i++)
- str[i] = i + '!';
-
- print("charset demo:\n");
- print(str);
- print("\n");
- init += find_char(init, '\n');
- } else {
- print("unknown command: ");
- usize idx = find_char(init, '\n');
- printn(init, idx);
- print("\n");
- init += idx;
- }
- }
+ unmask_irq(1);
+ enable_irqs();
- free(init_orig);
- halt();
+ thread_sched(nil, nil);
+ freeze();
}
diff --git a/stage3/pci.c b/stage3/pci.c
index 164071d..d764171 100644
--- a/stage3/pci.c
+++ b/stage3/pci.c
@@ -30,11 +30,11 @@ void pci_enumerate()
outl(PCI_CONFIG_ADDRESS, BITCAST(addr, pci_config_addr, u32));
u32 x = inl(PCI_CONFIG_DATA);
if (x != 0xFFFFFFFF) {
- print("bus: "); print_num(bus, 16, 2);
- print(" dev: "); print_num(dev, 16, 1);
- print(" vendor: "); print_num(x & 0xFFFF, 16, 4);
- print(" id: "); print_num((x >> 16) & 0xFFFF, 16, 4);
- print("\n");
+ print(S("bus: ")); print_num_pad(bus, 16, 2, ' ');
+ print(S(" dev: ")); print_num_pad(dev, 16, 1, ' ');
+ print(S(" vendor: ")); print_num_pad(x & 0xFFFF, 16, 4, ' ');
+ print(S(" id: ")); print_num_pad((x >> 16) & 0xFFFF, 16, 4, ' ');
+ print(S("\n"));
}
}
}
diff --git a/stage3/pic.c b/stage3/pic.c
index 726a6da..4ece7c3 100644
--- a/stage3/pic.c
+++ b/stage3/pic.c
@@ -19,6 +19,11 @@ void ack_irq(u8 lane)
outb(IO_PIC1_CTRL, 1 << 5);
}
+void wait_irq()
+{
+ asm volatile("hlt");
+}
+
void unmask_irq(u8 lane)
{
u8 port = IO_PIC1_DATA;
diff --git a/stage3/pic.h b/stage3/pic.h
index eebe119..6afb919 100644
--- a/stage3/pic.h
+++ b/stage3/pic.h
@@ -26,6 +26,7 @@ void disable_irqs();
void enable_irqs();
void unmask_irq(u8 lane);
void ack_irq(u8 lane);
+void wait_irq();
void pic_init();
#endif
diff --git a/stage3/ps2.c b/stage3/ps2.c
new file mode 100644
index 0000000..61f3582
--- /dev/null
+++ b/stage3/ps2.c
@@ -0,0 +1,205 @@
+#include "ps2.h"
+#include "def.h"
+#include "io.h"
+#include "font.h"
+#include "halt.h"
+
+// this should probably be refactored some time - i dont have the energy rn
+
+typedef enum {
+ PS2_CMD_READ_INTERNAL = 0x20,
+ PS2_CMD_WRITE_INTERNAL = 0x60,
+ PS2_CMD_DISABLE_PORT2 = 0xA7,
+ PS2_CMD_ENABLE_PORT2 = 0xA8,
+ PS2_CMD_TEST_PORT2 = 0xA9,
+ PS2_CMD_TEST_CTRL = 0xAA,
+ PS2_CMD_TEST_PORT1 = 0xAB,
+ PS2_CMD_DIAGNOSTIC_DUMP = 0xAC,
+ PS2_CMD_DISABLE_PORT1 = 0xAD,
+ PS2_CMD_ENABLE_PORT1 = 0xAE,
+ PS2_CMD_READ_CTRL_IN = 0xC0,
+ PS2_CMD_COPY_TO_STATUS_LOW = 0xC1,
+ PS2_CMD_COPY_TO_STATUS_HIGH = 0xC2,
+ PS2_CMD_READ_CTRL_OUT = 0xD0,
+ PS2_CMD_WRITE_CTRL_OUT = 0xD1,
+ PS2_CMD_WRITE_PORT1_OUT = 0xD2,
+ PS2_CMD_WRITE_PORT2_OUT = 0xD3,
+ PS2_CMD_WRITE_PORT2_IN = 0xD4,
+ PS2_CMD_PULSE_OUT = 0xF0,
+} ps2_cmd;
+
+typedef enum {
+ PS2_STATUS_OUT = 1 >> 0,
+ PS2_STATUS_IN = 1 >> 1,
+ PS2_STATUS_POST = 1 >> 2,
+ PS2_STATUS_RECEIVER = 1 >> 3,
+ PS2_STATUS_ERR_TIMEOUT = 1 >> 6,
+ PS2_STATUS_ERR_PARITY = 1 >> 7,
+} Ps2Status;
+
+typedef struct {
+ bool output : 1;
+ bool input : 1;
+ bool post : 1;
+ bool receiver : 1;
+ bool unknown_1 : 1;
+ bool unknown_2 : 1;
+ bool err_timeout : 1;
+ bool err_parity : 1;
+} __attribute__((packed)) ps2_status;
+
+typedef struct {
+ bool int_port1 : 1;
+ bool int_port2 : 1;
+ bool post : 1;
+ bool zero_1 : 1;
+ bool clk_port1 : 1;
+ bool clk_port2 : 1;
+ bool translation : 1;
+ bool zero_2 : 1;
+} __attribute__((packed)) ps2_ctrl_cfg;
+
+typedef enum {
+ PS2_CFG_INT_PORT1 = 1 >> 0,
+ PS2_CFG_INT_PORT2 = 1 >> 1,
+ PS2_CFG_POST = 1 >> 2,
+ // PS2_CFG_ZERO1 = 1 >> 3,
+ PS2_CFG_CLK_PORT1 = 1 >> 4,
+ PS2_CFG_CLK_PORT2 = 1 >> 5,
+ PS2_CFG_TRANS_PORT1 = 1 >> 6,
+ // PS2_CFG_ZERO2 = 1 >> 7,
+} Ps2CtrlCfg;
+
+typedef enum {
+ PS2_CTRL_SYSRESET = 1 >> 0,
+ PS2_CTRL_A20 = 1 >> 1,
+ PS2_CTRL_CLK_PORT2 = 1 >> 2,
+ PS2_CTRL_DATA_PORT2 = 1 >> 3,
+ PS2_CTRL_OUT_PORT1 = 1 >> 4,
+ PS2_CTRL_OUT_PORT2 = 1 >> 5,
+ PS2_CTRL_CLK_PORT1 = 1 >> 6,
+ PS2_CTRL_DATA_PORT1 = 1 >> 7,
+} Ps2CtrlOut;
+
+typedef enum {
+ PS2_TEST_CTRL_PASS = 0x55,
+ PS2_TEST_CTRL_FAIL = 0xFC,
+} Ps2TestCtrl;
+
+typedef enum {
+ PS2_TEST_PORT_PASS = 0x00,
+ PS2_TEST_PORT_CLK_STUCK_LOW = 0x01,
+ PS2_TEST_PORT_CLK_STUCK_HIGH = 0x02,
+ PS2_TEST_PORT_DATA_STUCK_LOW = 0x03,
+ PS2_TEST_PORT_DATA_STUCK_HIGH = 0x04,
+} Ps2TestPort;
+
+void ps2_poll_status(u8 mask, bool set)
+{
+ // TODO: wait instead of busy polling
+ // TODO: timeout
+ while (set == !(inb(IO_PS2_CTRL) & mask))
+ ;
+}
+
+u8 ps2_read_data()
+{
+ ps2_poll_status(PS2_STATUS_OUT, true);
+ return inb(IO_PS2_DATA);
+}
+
+void ps2_write_data(u8 val)
+{
+ ps2_poll_status(PS2_STATUS_IN, false);
+ outb(IO_PS2_DATA, val);
+}
+
+void ps2_write_ctrl(u8 val)
+{
+ ps2_poll_status(PS2_STATUS_IN, false);
+ outb(IO_PS2_CTRL, val);
+}
+
+u8 ps2_read_mem(u8 byte)
+{
+ ps2_write_ctrl(PS2_CMD_READ_INTERNAL + byte);
+ return ps2_read_data();
+}
+
+void ps2_write_mem(u8 byte, u8 val)
+{
+ ps2_write_ctrl(PS2_CMD_WRITE_INTERNAL + byte);
+ ps2_write_data(val);
+}
+
+void ps2_test_port(ps2_cmd cmd, str desc)
+{
+ ps2_write_ctrl(cmd);
+ print(S("PS/2 ")); print(desc); print(S(" test: "));
+ u8 b = ps2_read_data();
+ switch (b) {
+ case PS2_TEST_PORT_PASS: print(S("pass\n")); break;
+ case PS2_TEST_PORT_CLK_STUCK_LOW: panic(S("clock stuck low\n")); break;
+ case PS2_TEST_PORT_CLK_STUCK_HIGH: panic(S("clock stuck high\n")); break;
+ case PS2_TEST_PORT_DATA_STUCK_LOW: panic(S("data stuck low\n")); break;
+ case PS2_TEST_PORT_DATA_STUCK_HIGH: panic(S("data stuck high\n")); break;
+ default: print_hex(b); panic(S(" (unknown response)\n")); break;
+ }
+}
+
+void ps2_init()
+{
+ // TODO: USB bullshit
+ // TODO: determine if exists
+
+ // disable devices
+ ps2_write_ctrl(PS2_CMD_DISABLE_PORT1);
+ ps2_write_ctrl(PS2_CMD_DISABLE_PORT2);
+
+ // flush output buffer
+ inb(IO_PS2_DATA);
+
+ // disable IRQs and translation
+ u8 config = ps2_read_mem(0);
+ config &= ~(PS2_CFG_INT_PORT1 | PS2_CFG_INT_PORT2 | PS2_CFG_TRANS_PORT1);
+ ps2_write_mem(0, config);
+
+ // perform self-test
+ ps2_write_ctrl(PS2_CMD_TEST_CTRL);
+ print(S("PS/2 controller test: "));
+ switch (ps2_read_data()) {
+ case PS2_TEST_CTRL_PASS: print(S("pass\n")); break;
+ case PS2_TEST_CTRL_FAIL: panic(S("fail\n")); break;
+ default: panic(S("unknown response\n")); break;
+ }
+ ps2_write_mem(0, config); // restore config byte
+
+ // check if dual
+ bool dual = !!(config & PS2_CFG_CLK_PORT2);
+ if (dual) {
+ ps2_write_ctrl(PS2_CMD_ENABLE_PORT2);
+ config = ps2_read_mem(0);
+ dual = !(config & PS2_CFG_CLK_PORT2);
+
+ if (dual)
+ ps2_write_ctrl(PS2_CMD_DISABLE_PORT2);
+ }
+ print(S("PS/2 second port ")); print(dual ? S("enabled\n") : S("disabled\n"));
+
+ // test devices
+ ps2_test_port(PS2_CMD_TEST_PORT1, S("port 1"));
+ if (dual)
+ ps2_test_port(PS2_CMD_TEST_PORT2, S("port 2"));
+
+ // enable devices
+ ps2_write_ctrl(PS2_CMD_ENABLE_PORT1);
+ if (dual)
+ ps2_write_ctrl(PS2_CMD_ENABLE_PORT2);
+
+ // enable IRQs
+ config |= PS2_CFG_INT_PORT1;
+ ps2_write_mem(0, config);
+
+ ps2_write_data(0xFF);
+ ps2_read_data();
+}
diff --git a/stage3/ps2.h b/stage3/ps2.h
new file mode 100644
index 0000000..428ee15
--- /dev/null
+++ b/stage3/ps2.h
@@ -0,0 +1,6 @@
+#ifndef PS2_H
+#define PS2_H
+
+void ps2_init();
+
+#endif
diff --git a/stage3/shell.c b/stage3/shell.c
new file mode 100644
index 0000000..f8d5ada
--- /dev/null
+++ b/stage3/shell.c
@@ -0,0 +1,172 @@
+#include "shell.h"
+#include "font.h"
+#include "heap.h"
+#include "fs.h"
+#include "gfx.h"
+#include "string.h"
+#include "pci.h"
+#include "memory.h"
+
+static void cmd_echo(str arg)
+{
+ print(arg);
+ print_char('\n');
+}
+
+static void cmd_cat(str arg)
+{
+ str f = fs_read(arg);
+
+ if (f.data == nil) {
+ print(S("cat: file not found: "));
+ print(arg);
+ print(S("\n"));
+ } else {
+ print(f);
+ free(f.data);
+ }
+}
+
+static void cmd_font(str arg)
+{
+ str f = fs_read(arg);
+
+ if (f.data == nil) {
+ print(S("font: file not found: "));
+ print(arg);
+ print(S("\n"));
+ } else {
+ if (f.len == 16*256)
+ font_load_blob(f.data);
+ else
+ print(S("font: invalid file size\n"));
+
+ free(f.data);
+ }
+}
+
+static void cmd_fontdemo()
+{
+ const u8 max = '~' - '!' + 1;
+ char buf[max];
+
+ for (u8 i = 0; i < max; i++)
+ buf[i] = i + '!';
+
+ print(S("fontdemo:\n"));
+ print((str) { max, buf });
+ print(S("\n"));
+}
+
+static void cmd_img(str arg)
+{
+ str f = fs_read(arg);
+
+ if (f.data == nil) {
+ print(S("img: file not found: "));
+ print(arg);
+ print(S("\n"));
+ } else {
+ if (f.len < 2 * sizeof(u32))
+ print(S("img: missing header\n"));
+ else {
+ u32 width = ((u32 *) f.data)[0];
+ u32 height = ((u32 *) f.data)[1];
+
+ if (f.len != 2 * sizeof(u32) + width * height * sizeof(color))
+ print(S("img: invalid file size\n"));
+ else
+ gfx_draw_img(gfx_info->width-width, 0, width, height,
+ (void *) (f.data + 2 * sizeof(u32)));
+ }
+
+ free(f.data);
+ }
+}
+
+static void cmd_lspci(str arg)
+{
+ (void) arg;
+ pci_enumerate();
+}
+
+static void cmd_run(str arg)
+{
+ shell_run_file(arg);
+}
+
+extern char keymap[256];
+
+static void cmd_loadkeys(str arg)
+{
+ str f = fs_read(arg);
+
+ if (f.data == nil) {
+ print(S("loadkeys: file not found: "));
+ print(arg);
+ print(S("\n"));
+ } else {
+ if (f.len == 256)
+ memcpy(keymap, f.data, 256);
+ else
+ print(S("loadkeys: invalid file size\n"));
+
+ free(f.data);
+ }
+}
+
+typedef struct {
+ str name;
+ void (*fn)(str arg);
+} command;
+
+static command registry[] = {
+ { S("echo"), &cmd_echo },
+ { S("cat"), &cmd_cat },
+ { S("font"), &cmd_font },
+ { S("fontdemo"), &cmd_fontdemo },
+ { S("img"), &cmd_img },
+ { S("lspci"), &cmd_lspci },
+ { S("run"), &cmd_run },
+ { S("loadkeys"), &cmd_loadkeys },
+};
+
+void shell_run_cmd(str cmd)
+{
+ str prog = str_split_walk(&cmd, S(" \t"));
+
+ if (prog.len == 0)
+ return;
+
+ for (usize i = 0; i < sizeof registry / sizeof *registry; i++) {
+ if (str_cmp(prog, registry[i].name) == 0) {
+ registry[i].fn(cmd);
+ return;
+ }
+ }
+
+ print(S("shell: unknown command: "));
+ print(prog);
+ print(S("\n"));
+}
+
+void shell_run_file(str filename)
+{
+ str f = fs_read(filename);
+
+ if (f.data == nil) {
+ print(S("shell: file not found: "));
+ print(filename);
+ print(S("\n"));
+ } else {
+ str iter = f;
+ for (;;) {
+ str cmd = str_split_walk(&iter, S("\n"));
+ if (cmd.data == nil)
+ break;
+ shell_run_cmd(cmd);
+ }
+
+ free(f.data);
+ }
+}
diff --git a/stage3/shell.h b/stage3/shell.h
new file mode 100644
index 0000000..2336d21
--- /dev/null
+++ b/stage3/shell.h
@@ -0,0 +1,9 @@
+#ifndef SHELL_H
+#define SHELL_H
+
+#include "def.h"
+
+void shell_run_cmd(str arg);
+void shell_run_file(str arg);
+
+#endif
diff --git a/stage3/string.c b/stage3/string.c
index ff4bfc3..2f95b99 100644
--- a/stage3/string.c
+++ b/stage3/string.c
@@ -1,19 +1,39 @@
#include "string.h"
+#include "memory.h"
+#include "heap.h"
-usize find_char(const char *str, char chr)
+isize str_cmp(str s1, str s2)
{
- usize ret = 0;
- while (*str != chr && *str != '\0')
- str++, ret++;
- return ret;
+ if (s1.len != s2.len)
+ return (isize) s1.len - (isize) s2.len;
+
+ return memcmp(s1.data, s2.data, s1.len);
+}
+
+static bool match_char(char c, str tokens)
+{
+ for (usize t = 0; t < tokens.len; t++)
+ if (c == tokens.data[t])
+ return true;
+
+ return false;
+}
+
+usize str_find(str s, str tokens)
+{
+ for (usize i = 0; i < s.len; i++)
+ if (match_char(s.data[i], tokens))
+ return i;
+
+ return s.len;
}
-u64 parse_num(u8 **str, u8 base, isize size)
+usize str_parse_num(str s, u8 base, u64 *x)
{
- u64 x = 0;
+ *x = 0;
- while (size-- != 0) {
- u8 c = **str;
+ for (usize i = 0; i < s.len; i++) {
+ u8 c = s.data[i];
u64 d;
if (c >= '0' && c <= '9')
@@ -23,34 +43,30 @@ u64 parse_num(u8 **str, u8 base, isize size)
else if (c >= 'A' && c <= 'z')
d = c - 'A';
else
- return x;
+ return i;
if (d >= base)
- return x;
+ return i;
- (*str)++;
- x = x * base + d;
+ *x = *x * base + d;
}
- return x;
+ return s.len;
}
-usize strlen(const char *str)
+str str_split_walk(str *s, str sep)
{
- return find_char(str, '\0');
-}
+ if (s->len == 0)
+ return NILS;
-int strcmp(const char *p1, const char *p2)
-{
- while (*p1 == *p2 && *p1 != '\0')
- p1++, p2++;
- return *p1 - *p2;
-}
+ usize x = str_find(*s, sep);
+ usize o = x + (x < s->len);
-int strncmp(const char *p1, const char *p2, usize size)
-{
- for (usize i = 0; i < size; i++)
- if (p1[i] != p2[i])
- return p1[i]-p2[i];
- return 0;
+ s->len -= o;
+ s->data += o;
+
+ if (x == 0)
+ return str_split_walk(s, sep);
+ else
+ return (str) { x, s->data - o };
}
diff --git a/stage3/string.h b/stage3/string.h
index 2738f60..8375136 100644
--- a/stage3/string.h
+++ b/stage3/string.h
@@ -3,10 +3,9 @@
#include "def.h"
-u64 parse_num(u8 **str, u8 base, isize size);
-usize find_char(const char *str, char chr);
-usize strlen(const char *str);
-int strcmp(const char *p1, const char *p2);
-int strncmp(const char *p1, const char *p2, usize size);
+isize str_cmp(str s1, str s2);
+usize str_find(str s, str tokens);
+usize str_parse_num(str s, u8 base, u64 *x);
+str str_split_walk(str *s, str sep);
#endif
diff --git a/stage3/thread.c b/stage3/thread.c
new file mode 100644
index 0000000..f78b80a
--- /dev/null
+++ b/stage3/thread.c
@@ -0,0 +1,82 @@
+#include "thread.h"
+#include "heap.h"
+#include "pic.h"
+
+static thread *current_thread = nil;
+void *thread_sched_stack = nil;
+
+static event_queue queue_read = { 0, 0, nil };
+event_queue queue_write = { 0, 0, nil };
+
+thread *irq_services[16] = { nil };
+
+void resume(void *stack, void *ret); // yield.asm
+
+void thread_resume(void *ret, thread *t)
+{
+ current_thread = t;
+ resume(t->stack, ret);
+}
+
+#define STACK_SIZE 0xffff
+
+thread *thread_create(str name, void *init)
+{
+ thread *t = malloc(sizeof *t);
+ t->name = name;
+ t->stack_bottom = malloc(STACK_SIZE);
+ t->stack = t->stack_bottom + STACK_SIZE - 8;
+ *(void **) t->stack = init;
+ t->stack -= 8*8;
+ return t;
+}
+
+void thread_sched(yield_arg *arg, void *stack)
+{
+ if (current_thread != nil)
+ current_thread->stack = stack;
+
+ if (arg == nil) {
+ // TODO: add to some sort of runqueue? (nil means not polling for anything)
+ } else if (arg->exit) {
+ free(current_thread->stack_bottom);
+ free(current_thread);
+ current_thread = nil;
+ } else if (arg->timeout >= 0) {
+ // TODO: meow
+ }
+
+ for (;;) {
+ if (queue_read.len == 0) {
+ disable_irqs();
+
+ // swap queues
+ event_queue tmp = queue_read;
+ queue_read = queue_write;
+ queue_write = tmp;
+
+ enable_irqs();
+ }
+
+ if (queue_read.len > 0) {
+ event *e = malloc(sizeof *e);
+ *e = queue_read.data[--queue_read.len];
+
+ if (irq_services[e->irq] == nil)
+ free(e); // *shrug*
+ else
+ // this never returns. callee must free e
+ thread_resume(e, irq_services[e->irq]);
+ }
+
+ wait_irq();
+ }
+}
+
+void thread_init()
+{
+ thread_sched_stack = malloc(STACK_SIZE);
+
+ queue_read = (event_queue) { 0, 1024, malloc(1024 * sizeof(event)) };
+ queue_write = (event_queue) { 0, 1024, malloc(1024 * sizeof(event)) };
+}
diff --git a/stage3/thread.h b/stage3/thread.h
new file mode 100644
index 0000000..9f5ff2f
--- /dev/null
+++ b/stage3/thread.h
@@ -0,0 +1,40 @@
+#ifndef THREAD_H
+#define THREAD_H
+
+#include "def.h"
+
+typedef struct {
+ bool exit;
+ i64 timeout;
+} yield_arg;
+
+void *yield(void *arg);
+
+typedef struct {
+ str name;
+ void *stack_bottom;
+ void *stack;
+} thread;
+
+typedef struct {
+ u8 irq;
+ union {
+ u8 scancode;
+ } data;
+} event;
+
+typedef struct {
+ usize len;
+ usize cap;
+ event *data;
+} event_queue;
+
+extern event_queue queue_write;
+extern thread *irq_services[16];
+
+void thread_init();
+void thread_resume(void *ret, thread *t);
+thread *thread_create(str name, void *init);
+void thread_sched(yield_arg *arg, void *stack);
+
+#endif
diff --git a/stage3/yield.asm b/stage3/yield.asm
new file mode 100644
index 0000000..c078f16
--- /dev/null
+++ b/stage3/yield.asm
@@ -0,0 +1,45 @@
+extern thread_sched, thread_sched_stack
+global yield, resume
+
+extern print_num, halt
+
+yield:
+ ; push rip (done using call)
+ push rbx
+ push rbp
+ push rdi
+ push rsi
+ push r12
+ push r13
+ push r14
+ push r15
+
+ cld
+ ; mov rdi, rdi
+ mov rsi, rsp
+ mov rsp, [thread_sched_stack]
+ jmp thread_sched
+
+; stack in rdi
+; return in rsi
+resume:
+ mov rsp, rdi
+ mov rax, rsi
+
+ pop r15
+ pop r14
+ pop r13
+ pop r12
+ pop rsi
+ pop rdi
+ pop rbp
+ pop rbx
+
+ ;mov rdi, [rsp]
+ ;mov rsi, 16
+ ;mov rdx, 0
+ ;call print_num
+ ;cli
+ ;call halt
+
+ ret ; pop rip
diff --git a/util/dump2cuddlekeys/.gitignore b/util/dump2cuddlekeys/.gitignore
new file mode 100644
index 0000000..ba69a71
--- /dev/null
+++ b/util/dump2cuddlekeys/.gitignore
@@ -0,0 +1 @@
+dump2cuddlekeys
diff --git a/util/dump2cuddlekeys/Makefile b/util/dump2cuddlekeys/Makefile
new file mode 100644
index 0000000..07b7bb2
--- /dev/null
+++ b/util/dump2cuddlekeys/Makefile
@@ -0,0 +1,8 @@
+dump2cuddlekeys: main.c
+ gcc -Wall -Wextra main.c -o dump2cuddlekeys
+
+dumpkeys:
+ dumpkeys -C/dev/tty1 | grep '^keycode' | less
+
+preview: map2cuddlekeys
+ dumpkeys -C/dev/tty1 | ./dump2cuddlekeys | hexdump -C
diff --git a/util/dump2cuddlekeys/main.c b/util/dump2cuddlekeys/main.c
new file mode 100644
index 0000000..ec0039e
--- /dev/null
+++ b/util/dump2cuddlekeys/main.c
@@ -0,0 +1,89 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+int main()
+{
+ char keymap[256] = { '\0' };
+
+ char *line = NULL;
+ size_t cap = 0;
+ ssize_t len;
+ while ((len = getline(&line, &cap, stdin)) != -1) {
+ int code;
+ char key[len+1];
+
+ if (sscanf(line, "keycode %d = %s", &code, key) != 2)
+ continue;
+
+ char c = '\0';
+
+ struct {
+ char c;
+ const char *name;
+ } defs[] = {
+ { '\n', "Return" },
+ { ' ', "space" },
+ { '\b', "Delete" },
+ { '!', "exclam" },
+ { '&', "ampersand" },
+ { '%', "percent" },
+ { '$', "dollar" },
+ { '\'', "apostrophe" },
+ { '(', "parenleft" },
+ { ')', "parenright" },
+ { '*', "asterisk" },
+ { '+', "plus" },
+ { ',', "comma" },
+ { '-', "minus" },
+ { '.', "period" },
+ { '/', "slash" },
+ { '0', "zero" },
+ { '1', "one" },
+ { '2', "two" },
+ { '3', "three" },
+ { '4', "four" },
+ { '5', "five" },
+ { '6', "six" },
+ { '7', "seven" },
+ { '8', "eight" },
+ { '9', "nine" },
+ { ':', "colon" },
+ { ';', "semicolon" },
+ { '<', "less" },
+ { '=', "equal" },
+ { '>', "greater" },
+ { '?', "question" },
+ { '\n', "linefeed" },
+ { '\\', "backslash" },
+ { '\t', "Tab" },
+ { '@', "at" },
+ { '[', "bracketleft" },
+ { ']', "bracketright" },
+ { '(', "{" },
+ { ')', "}" },
+ { '#', "numbersign" },
+ { '^', "asciicircum" },
+ };
+
+ if (strlen(key) == 1)
+ c = key[0];
+ else if (strlen(key) == 2 && key[0] == '+')
+ c = key[1];
+ else for (size_t i = 0; i < sizeof defs / sizeof *defs; i++)
+ if (strcmp(defs[i].name, key) == 0) {
+ c = defs[i].c;
+ break;
+ }
+
+ if (c != '\0')
+ keymap[(unsigned char) code] = (char) c;
+ else if (strcmp(key, "nul") != 0)
+ {} // fprintf(stderr, "unhandled: %s\n", key);
+ }
+
+ if (line != NULL)
+ free(line);
+
+ fwrite(keymap, 1, 256, stdout);
+}