diff --git a/include/calcite/syscalls.h b/include/calcite/syscalls.h
index 2ed46f0..65f7a38 100644
--- a/include/calcite/syscalls.h
+++ b/include/calcite/syscalls.h
@@ -36,3 +36,5 @@ enum fs_access_result read_file_splat(file_handle_t handle, void *buffer, uint64
void wait_for_mouse_packet(struct mouse_packet *packet_out);
void *map_pages(uint64_t count);
+
+void sleep_ms(uint64_t ms_to_sleep);
diff --git a/include/kernel-public/syscall-numbers.h b/include/kernel-public/syscall-numbers.h
index 22d25bd..24440bc 100644
--- a/include/kernel-public/syscall-numbers.h
+++ b/include/kernel-public/syscall-numbers.h
@@ -25,5 +25,6 @@ enum {
SYSCALL_GET_FILE_SIZE,
SYSCALL_READ_FILE,
SYSCALL_WAIT_FOR_MOUSE_PACKET,
- SYSCALL_MAP_PAGES
+ SYSCALL_MAP_PAGES,
+ SYSCALL_SLEEP_MS
};
diff --git a/src/kernel/entry.c b/src/kernel/entry.c
index 87af06f..272d4b2 100644
--- a/src/kernel/entry.c
+++ b/src/kernel/entry.c
@@ -25,6 +25,7 @@
#include "paging.h"
#include "input.h"
#include "panic.h"
+#include "timer.h"
#include "heap.h"
#include "pata.h"
#include "ps2.h"
@@ -243,9 +244,13 @@ static const char *cmdline_look_up(const char *key) {
//set up interrupts
+ init_timer();
init_ps2();
+
+ set_irq_handler(0x00, &on_pit_irq);
set_irq_handler(0x01, &on_keyboard_irq);
set_irq_handler(0x0c, &on_mouse_irq);
+
enable_interrupts();
//set up syscalls
@@ -259,6 +264,7 @@ static const char *cmdline_look_up(const char *key) {
register_syscall(SYSCALL_READ_FILE, (void *)&syscall_read_file);
register_syscall(SYSCALL_WAIT_FOR_MOUSE_PACKET, (void *)&syscall_wait_for_mouse_packet);
register_syscall(SYSCALL_MAP_PAGES, (void *)&syscall_map_pages);
+ register_syscall(SYSCALL_SLEEP_MS, (void *)&syscall_sleep_ms);
//probe for drives
diff --git a/src/kernel/timer.asm b/src/kernel/timer.asm
new file mode 100644
index 0000000..45e1c27
--- /dev/null
+++ b/src/kernel/timer.asm
@@ -0,0 +1,37 @@
+ ; Calcite, src/kernel/timer.asm
+ ; Copyright 2025 Benji Dial
+ ;
+ ; This program 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 of the License, or
+ ; (at your option) any later version.
+ ;
+ ; This program 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.
+ ;
+ ; You should have received a copy of the GNU General Public License along
+ ; with this program. If not, see .
+
+
+bits 64
+default rel
+
+section .text
+
+global init_timer
+init_timer:
+
+ ;generate an irq every 1193 cycles (~ every millisecond)
+
+ mov al, 0x34
+ out 0x43, al
+
+ mov al, 0xa9
+ out 0x40, al
+
+ mov al, 0x04
+ out 0x40, al
+
+ ret
diff --git a/src/kernel/timer.c b/src/kernel/timer.c
new file mode 100644
index 0000000..405da78
--- /dev/null
+++ b/src/kernel/timer.c
@@ -0,0 +1,93 @@
+/* Calcite, src/kernel/timer.c
+ * Copyright 2025 Benji Dial
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+#include "scheduler.h"
+#include "timer.h"
+#include "heap.h"
+
+struct sleeping_list_entry {
+ struct sleeping_list_entry *next;
+ uint64_t sleeping_until;//pit irqs since boot
+ struct continuation_info continuation;
+};
+
+//entries are always sorted increasing by sleeping_until
+static struct sleeping_list_entry *first_entry = 0;
+static struct sleeping_list_entry *last_entry = 0;
+
+//pit irq happens ~ once per millisecond
+static uint64_t pit_irqs_since_boot = 0;
+
+void on_pit_irq() {
+
+ ++pit_irqs_since_boot;
+
+ while (first_entry != 0 && pit_irqs_since_boot >= first_entry->sleeping_until) {
+
+ add_to_queue(&ready_continuations, &first_entry->continuation);
+
+ struct sleeping_list_entry *old_first = first_entry;
+ first_entry = old_first->next;
+ heap_dealloc(old_first, sizeof(struct sleeping_list_entry));
+
+ if (first_entry == 0)
+ last_entry = 0;
+
+ }
+
+}
+
+//until in pit irqs since boot
+static void sleep_until(uint64_t until) {
+
+ struct sleeping_list_entry *entry = heap_alloc(sizeof(struct sleeping_list_entry));
+ entry->sleeping_until = until;
+
+ __asm__ ("cli");
+
+ if (first_entry == 0) {
+ entry->next = 0;
+ first_entry = entry;
+ last_entry = entry;
+ }
+
+ else if (first_entry->sleeping_until >= until) {
+ entry->next = first_entry;
+ first_entry = entry;
+ }
+
+ else if (last_entry->sleeping_until <= until) {
+ entry->next = 0;
+ last_entry->next = entry;
+ last_entry = entry;
+ }
+
+ else {
+ struct sleeping_list_entry *just_before = first_entry;
+ while (just_before->next->sleeping_until <= until)
+ just_before = just_before->next;
+ entry->next = just_before->next;
+ just_before->next = entry;
+ }
+
+ yield_sti(&entry->continuation);
+
+}
+
+void syscall_sleep_ms(uint64_t ms_to_sleep) {
+ sleep_until(pit_irqs_since_boot + ms_to_sleep);
+}
diff --git a/src/kernel/timer.h b/src/kernel/timer.h
new file mode 100644
index 0000000..9e53a09
--- /dev/null
+++ b/src/kernel/timer.h
@@ -0,0 +1,26 @@
+/* Calcite, src/kernel/timer.h
+ * Copyright 2025 Benji Dial
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+#pragma once
+
+#include
+
+void init_timer();
+
+void on_pit_irq();
+
+void syscall_sleep_ms(uint64_t ms_to_sleep);
diff --git a/src/user-apps/hello/hello.c b/src/user-apps/hello/hello.c
index 744abfb..069b78a 100644
--- a/src/user-apps/hello/hello.c
+++ b/src/user-apps/hello/hello.c
@@ -76,6 +76,8 @@ void main() {
while (1) {
+ sleep_ms(10);
+
struct mouse_packet packet;
wait_for_mouse_packet(&packet);
diff --git a/src/user-libs/calcite/syscalls.c b/src/user-libs/calcite/syscalls.c
index 013ae67..80a5b7f 100644
--- a/src/user-libs/calcite/syscalls.c
+++ b/src/user-libs/calcite/syscalls.c
@@ -60,3 +60,7 @@ void wait_for_mouse_packet(struct mouse_packet *packet_out) {
void *map_pages(uint64_t count) {
return (void *)do_syscall(count, 0, 0, SYSCALL_MAP_PAGES);
}
+
+void sleep_ms(uint64_t ms_to_sleep) {
+ do_syscall(ms_to_sleep, 0, 0, SYSCALL_SLEEP_MS);
+}