commit 664d56875223ab8cb8de82892aad2dcc33bc7012
parent 3b623972a8fd45b2b3b28daafd72d754a852a805
Author: Christos Margiolis <christos@margiolis.net>
Date: Fri, 16 Jul 2021 03:28:10 +0300
organizing stuff
Diffstat:
35 files changed, 859 insertions(+), 822 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,11 +1,11 @@
TGTDIR = build
all:
- cd boot && make install clean && cd -
+ cd kern && make install clean && cd -
run:
qemu-system-i386 -hdd ${TGTDIR}/os.bin
clean:
rm -rf ${TGTDIR}
- cd boot && make clean && cd -
+ cd kern && make clean && cd -
diff --git a/README b/README
@@ -1,5 +1,6 @@
My OS
=====
+
Linux users are afraid of it.
Build
diff --git a/boot/Makefile b/boot/Makefile
@@ -1,51 +0,0 @@
-BIN = os.bin
-BINDIR = ../build
-
-CC = cc
-ASM = nasm
-LD = ld
-CFLAGS = -g -m32 -nostdlib -ffreestanding -Wall -Wextra -std=c99 -O2
-LDFLAGS = -Ttext 0x1000 --oformat binary
-
-BOOTFILE = boot.asm
-BOOT_BIN = boot.bin
-KERNEL_BIN = kernel.bin
-SRC = *.c *.asm
-OBJ = kmain.o \
- string.o \
- tty.o \
- idt.o \
- int.o \
- kbd.o \
- timer.o
-
-all: options ${BIN}
-
-options:
- @echo ${BIN} build options
- @echo CC = ${CC}
- @echo ASM = ${ASM}
- @echo LD = ${LD}
- @echo CFLAGS = ${CFLAGS}
- @echo LDFLAGS = ${LDFLAGS}
-
-${BIN}: ${OBJ}
- mkdir -p ${BINDIR}
- ${ASM} -fbin ${BOOTFILE} -o ${BOOT_BIN}
- ${LD} ${LDFLAGS} ${OBJ} -o ${KERNEL_BIN}
- dd if=/dev/zero bs=1000000 count=1 >> ${KERNEL_BIN}
- cat ${BOOT_BIN} ${KERNEL_BIN} > $@
-
-.c.o:
- ${CC} -c ${CFLAGS} $<
-
-.asm.o:
- ${ASM} -felf $<
-
-install: all
- mv ${BIN} ${BINDIR}
-
-clean:
- rm -rf *.bin *.o
-
-.PHONY: all options install clean
diff --git a/boot/extern.h b/boot/extern.h
@@ -1,21 +0,0 @@
-#ifndef _KERNEL_EXTERN_H_
-#define _KERNEL_EXTERN_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "stdarg.h"
-#include "io.h"
-#include "tty.h"
-#include "string.h"
-
-#define BUFSIZ 1024
-
-#define ARRLEN(x) (sizeof(x) / sizeof((x)[0]))
-#define sizeof_field(s, f) (sizeof(((t *)0)->f))
-
-int vsprintf(char *, const char *, va_list);
-int sprintf(char *, const char *, ...);
-int printf(const char *, ...);
-
-#endif /* _KERNEL_EXTERN_H_ */
diff --git a/boot/idt.c b/boot/idt.c
@@ -1,166 +0,0 @@
-#include "extern.h"
-#include "idt.h"
-#include "port.h"
-
-#define N_INT 256
-#define KERN_CODESEG 0x08
-
-static void idt_set_gate(uint8_t, uint32_t);
-
-static struct idt_gate idt[N_INT];
-static void *isr[16] = {NULL};
-/* Exception messages for the first 32 interrupts. */
-static const char *except[] = {
- "Division By Zero Exception",
- "Debug Exception",
- "Non Maskable Interrupt Exception",
- "Breakpoint Exception",
- "Into Detected Overflow Exception",
- "Out of Bounds Exception",
- "Invalid Opcode Exception",
- "No Coprocessor Exception",
- "Double Fault Exception",
- "Coprocessor Segment Overrun Exception",
- "Bad TSS Exception",
- "Segment Not Present Exception",
- "Stack Fault Exception",
- "General Protection Fault Exception",
- "Page Fault Exception",
- "Unknown Interrupt Exception",
- "Coprocessor Fault Exception",
- "Alignment Check Exception (486+)",
- "Machine Check Exception (Pentium/586+)",
- "Reserved Exception",
- "Reserved Exception",
- "Reserved Exception",
- "Reserved Exception",
- "Reserved Exception",
- "Reserved Exception",
- "Reserved Exception",
- "Reserved Exception",
- "Reserved Exception",
- "Reserved Exception",
- "Reserved Exception",
- "Reserved Exception",
- "Reserved Exception",
-};
-
-static void
-idt_set_gate(uint8_t n, uint32_t ptr)
-{
- idt[n].off_lo = ptr & 0xffff;
- idt[n].sel = KERN_CODESEG;
- idt[n].zero = 0;
- /* The gate is present and is running in kernel mode. */
- idt[n].flags = 0x8e;
- idt[n].off_hi = (ptr >> 16) & 0xffff;
-}
-
-void
-idt_init(void)
-{
- struct idt_reg {
- uint16_t limit; /* IDT size in bytes. */
- uint32_t base; /* Points at `idt[0]`. */
- } __attribute__((packed)) idtr;
-
- (void)memset(&idt, 0, sizeof(idt));
-
- /* Set up exception interrupts. */
- idt_set_gate(0, (uint32_t)ex0);
- idt_set_gate(1, (uint32_t)ex1);
- idt_set_gate(2, (uint32_t)ex2);
- idt_set_gate(3, (uint32_t)ex3);
- idt_set_gate(4, (uint32_t)ex4);
- idt_set_gate(5, (uint32_t)ex5);
- idt_set_gate(6, (uint32_t)ex6);
- idt_set_gate(7, (uint32_t)ex7);
- idt_set_gate(8, (uint32_t)ex8);
- idt_set_gate(9, (uint32_t)ex9);
- idt_set_gate(10, (uint32_t)ex10);
- idt_set_gate(11, (uint32_t)ex11);
- idt_set_gate(12, (uint32_t)ex12);
- idt_set_gate(13, (uint32_t)ex13);
- idt_set_gate(14, (uint32_t)ex14);
- idt_set_gate(15, (uint32_t)ex15);
- idt_set_gate(16, (uint32_t)ex16);
- idt_set_gate(17, (uint32_t)ex17);
- idt_set_gate(18, (uint32_t)ex18);
- idt_set_gate(19, (uint32_t)ex19);
- idt_set_gate(20, (uint32_t)ex20);
- idt_set_gate(21, (uint32_t)ex21);
- idt_set_gate(22, (uint32_t)ex22);
- idt_set_gate(23, (uint32_t)ex23);
- idt_set_gate(24, (uint32_t)ex24);
- idt_set_gate(25, (uint32_t)ex25);
- idt_set_gate(26, (uint32_t)ex26);
- idt_set_gate(27, (uint32_t)ex27);
- idt_set_gate(28, (uint32_t)ex28);
- idt_set_gate(29, (uint32_t)ex29);
- idt_set_gate(30, (uint32_t)ex30);
- idt_set_gate(31, (uint32_t)ex31);
-
- /* Remap the PIC */
- /* TODO: explain.*/
- outb(P_PIC1_CMD, 0x11);
- outb(P_PIC2_CMD, 0x11);
- outb(P_PIC1_DATA, 0x20);
- outb(P_PIC2_DATA, 0x28);
- outb(P_PIC1_DATA, 0x04);
- outb(P_PIC2_DATA, 0x02);
- outb(P_PIC1_DATA, 0x01);
- outb(P_PIC2_DATA, 0x01);
- outb(P_PIC1_DATA, 0x00);
- outb(P_PIC2_DATA, 0x00);
-
- /* Set up IRQs */
- idt_set_gate(32, (uint32_t)irq0);
- idt_set_gate(33, (uint32_t)irq1);
- idt_set_gate(34, (uint32_t)irq2);
- idt_set_gate(35, (uint32_t)irq3);
- idt_set_gate(36, (uint32_t)irq4);
- idt_set_gate(37, (uint32_t)irq5);
- idt_set_gate(38, (uint32_t)irq6);
- idt_set_gate(39, (uint32_t)irq7);
- idt_set_gate(40, (uint32_t)irq8);
- idt_set_gate(41, (uint32_t)irq9);
- idt_set_gate(42, (uint32_t)irq10);
- idt_set_gate(43, (uint32_t)irq11);
- idt_set_gate(44, (uint32_t)irq12);
- idt_set_gate(45, (uint32_t)irq13);
- idt_set_gate(46, (uint32_t)irq14);
- idt_set_gate(47, (uint32_t)irq15);
-
- /* https://wiki.osdev.org/Interrupt_Descriptor_Table#Location_and_Size */
- idtr.base = (uint32_t)&idt;
- idtr.limit = N_INT * sizeof(struct idt_gate) - 1;
- __asm__ __volatile__ ("lidtl (%0)" : : "r" (&idtr));
-}
-
-void
-int_handler(struct reg *r)
-{
- void (*handler)(struct reg *);
-
- /*
- * We'll call the handler only if the interrupt number is > 32,
- * which means that we're dealing with an IRQ and not an exception.
- */
- if (r->intno >= 32 && (handler = isr[r->intno - 32]) != NULL)
- handler(r);
- /* Entries below index 32 in the IDT are exceptions, we need to hang. */
- else if (r->intno < 32) {
- tty_write(except[r->intno]);
- tty_write(". System halted...\n");
- __asm__ __volatile__ ("hlt");
- }
- if (r->intno >= 40)
- outb(P_PIC2_CMD, 0x20);
- outb(P_PIC1_CMD, 0x20);
-}
-
-void
-int_add_handler(uint8_t intno, void (*handler)(struct reg *r))
-{
- isr[intno] = handler;
-}
diff --git a/boot/idt.h b/boot/idt.h
@@ -1,99 +0,0 @@
-#ifndef _KERNEL_IDT_H_
-#define _KERNEL_IDT_H_
-
-/* IA-32 */
-struct idt_gate {
- uint16_t off_lo;
- uint16_t sel;
- uint8_t zero;
- uint8_t flags;
- uint16_t off_hi;
-} __attribute__((packed));
-
-/* This will be populated by `int_common_stub` in `int.asm`. */
-struct reg {
- /* Will be popped last. */
- uint32_t gs;
- uint32_t fs;
- uint32_t es;
- uint32_t ds;
- /* Pushed by `pusha`. */
- uint32_t edi;
- uint32_t esi;
- uint32_t ebp;
- uint32_t esp;
- uint32_t ebx;
- uint32_t edx;
- uint32_t ecx;
- uint32_t eax;
- /* Interrupt info. Pushed by `push byte`. */
- uint32_t intno;
- uint32_t err;
- /* Pushed by the CPU. */
- uint32_t eip;
- uint32_t cs;
- uint32_t eflags;
- uint32_t uesp;
- uint32_t ss;
-};
-
-/* Called by `kmain`. */
-void idt_init(void);
-
-/* Called by drivers. */
-void int_handler(struct reg *);
-void int_add_handler(uint8_t, void (*)(struct reg *));
-
-/* The first 32 interrupts are reserved for exceptions. */
-void ex0(void);
-void ex1(void);
-void ex2(void);
-void ex3(void);
-void ex4(void);
-void ex5(void);
-void ex6(void);
-void ex7(void);
-void ex8(void);
-void ex9(void);
-void ex10(void);
-void ex11(void);
-void ex12(void);
-void ex13(void);
-void ex14(void);
-void ex15(void);
-void ex16(void);
-void ex17(void);
-void ex18(void);
-void ex19(void);
-void ex20(void);
-void ex21(void);
-void ex22(void);
-void ex23(void);
-void ex24(void);
-void ex25(void);
-void ex26(void);
-void ex27(void);
-void ex28(void);
-void ex29(void);
-void ex30(void);
-void ex31(void);
-
-/* IRQs */
-void irq0(void);
-void irq1(void);
-void irq2(void);
-void irq3(void);
-void irq4(void);
-void irq5(void);
-void irq6(void);
-void irq7(void);
-void irq8(void);
-void irq9(void);
-void irq10(void);
-void irq11(void);
-void irq12(void);
-void irq13(void);
-void irq14(void);
-void irq15(void);
-
-#endif /* _KERNEL_IDT_H_ */
diff --git a/boot/io.h b/boot/io.h
@@ -1,51 +0,0 @@
-#ifndef _KERNEL_IO_H_
-#define _KERNEL_IO_H_
-
-#include <stdint.h>
-
-static inline uint8_t
-inb(uint16_t port)
-{
- uint8_t res;
-
- __asm__ __volatile__ ("in %%dx, %%al" : "=a" (res) : "d" (port));
- return res;
-}
-
-static inline void
-outb(uint16_t port, uint8_t v)
-{
- __asm__ __volatile__ ("out %%al, %%dx" : : "a" (v), "d" (port));
-}
-
-static inline uint16_t
-inw(uint16_t port)
-{
- uint16_t res;
-
- __asm__ __volatile__ ("in %%dx, %%ax" : "=a" (res) : "d" (port));
- return res;
-}
-
-static inline void
-outw(uint16_t port, uint16_t v)
-{
- __asm__ __volatile__ ("out %%ax, %%dx" : : "a" (v), "d" (port));
-}
-
-static inline uint32_t
-inl(uint16_t port)
-{
- uint32_t res;
-
- __asm__ __volatile__ ("in %%dx, %%eax" : "=a" (res) : "d" (port));
- return res;
-}
-
-static inline void
-outl(uint16_t port, uint32_t v)
-{
- __asm__ __volatile__ ("out %%eax, %%dx" : : "a" (v), "d" (port));
-}
-
-#endif /* _KERNEL_IO_H_ */
diff --git a/boot/kbd.c b/boot/kbd.c
@@ -1,61 +0,0 @@
-#include "extern.h"
-#include "idt.h"
-#include "port.h"
-
-static void kbd_callback(struct reg *);
-
-static unsigned char kbdus[128] = {
- 0, /* Error */
- 27, /* Escape */
- '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
- '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p',
- '[', ']', '\n',
- 0, /* Control */
- 'a', 's', 'd', 'f', 'g',
- 'h', 'j', 'k', 'l', ';', '\'', '`',
- 0, /* Left Shift */
- '\\', 'z', 'x',
- 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
- 0, /* Right Shift */
- '*',
- 0, /* Alt */
- ' ',
- 0, /* Caps Lock */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F1-F10 */
- 0, /* Num Lock */
- 0, /* Scroll Lock */
- 0, /* Home Key */
- 0, /* Up Arrow */
- 0, /* Page Up */
- '-',
- 0, /* Left Arrow */
- 0,
- 0, /* Right Arrow */
- '+',
- 0, /* End Key */
- 0, /* Down Arrow */
- 0, /* Page Down */
- 0, /* Insert Key */
- 0, /* Delete Key */
- 0, 0, 0,
- 0, 0, /* F11, F12 */
- 0, /* The rest are undefined */
-};
-
-static void
-kbd_callback(struct reg *r)
-{
- uint8_t sc;
-
- if ((sc = inb(P_KBD)) & 0x80) {
- } else {
- tty_putc(kbdus[sc]);
- }
- (void)r;
-}
-
-void
-kbd_init(void)
-{
- int_add_handler(1, kbd_callback);
-}
diff --git a/boot/kbd.h b/boot/kbd.h
@@ -1,8 +0,0 @@
-#ifndef _KERNEL_KBD_H_
-#define _KERNEL_KBD_H_
-
-#include "extern.h"
-
-void kbd_init(void);
-
-#endif /* _KERNEL_KBD_H_ */
diff --git a/boot/kmain.c b/boot/kmain.c
@@ -1,16 +0,0 @@
-#include "extern.h"
-#include "idt.h"
-#include "kbd.h"
-#include "timer.h"
-
-void
-kmain(void)
-{
- tty_clear();
- idt_init();
- timer_init(50);
- kbd_init();
- __asm__ __volatile__ ("sti");
-
- for (;;);
-}
diff --git a/boot/printf.c b/boot/printf.c
@@ -1,88 +0,0 @@
-#include "extern.h"
-
-static char *
-itoa(int n, char *buf, int base)
-{
- (void)n;
- (void)base;
-
- return buf;
-}
-
-int
-vsprintf(char *buf, const char *fmt, va_list args)
-{
- char *str, *s;
- int base, i, n;
-
- for (str = buf; *fmt != '\0'; fmt++) {
- base = 10;
- if (*fmt != '%') {
- *str++ = *fmt;
- continue;
- }
- if (*fmt == '%') {
- switch (*(++fmt)) {
- case 'c':
- *str++ = (unsigned char)va_arg(args, int);
- continue;
- case 's':
- s = va_arg(args, char *);
- n = strlen(s);
- for (i = 0; i < n; i++)
- *str++ = *s++;
- continue;
- case 'p':
- continue;
- case 'd': /* FALLTHROUGH */
- case 'i':
- n = va_arg(args, int);
- break;
- case 'u':
- n = va_arg(args, unsigned int);
- break;
- case 'o':
- base = 8;
- break;
- case 'X':
- base = 16;
- break;
- case '%':
- *str++ = '%';
- continue;
- }
- str = itoa(n, str, base);
- }
- }
- *str = '\0';
-
- return str - buf;
-}
-
-int
-sprintf(char *buf, const char *fmt, ...)
-{
- va_list args;
- int n;
-
- va_start(args, fmt);
- n = vsprintf(buf, fmt, args);
- va_end(args);
-
- return n;
-}
-
-int
-printf(const char *fmt, ...)
-{
- char buf[BUFSIZ];
- va_list args;
- int n;
-
- va_start(args, fmt);
- n = vsprintf(buf, fmt, args);
- va_end(args);
- tty_write(buf);
-
- return n;
-}
diff --git a/boot/string.c b/boot/string.c
@@ -1,45 +0,0 @@
-#include "string.h"
-
-void *
-memset(void *dst, int v, size_t len)
-{
- unsigned char *dst0;
-
- dst0 = dst;
- while (len--)
- *dst0++ = v;
- return dst;
-}
-
-void *
-memcpy(void *dst, const void *src, size_t len)
-{
- const unsigned char *src0;
- unsigned char *dst0;
-
- src0 = src;
- dst0 = dst;
-
- while (len--)
- *dst0++ = *src0++;
- return dst;
-}
-
-size_t
-strlen(const char *str)
-{
- const char *s;
-
- for (s = str; *s != '\0'; s++)
- ;
- return (s - str);
-}
-
-int
-strcmp(const char *s1, const char *s2)
-{
- while (*s1 == *s2++)
- if (*s1++ == '\0')
- return 0;
- return *(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1);
-}
diff --git a/boot/string.h b/boot/string.h
@@ -1,11 +0,0 @@
-#ifndef _KERNEL_STRING_H_
-#define _KERNEL_STRING_H_
-
-#include <stddef.h>
-
-void *memset(void *, int, size_t);
-void *memcpy(void *, const void *, size_t);
-size_t strlen(const char *);
-int strcmp(const char *, const char *);
-
-#endif /* _KERNEL_STRING_H_ */
diff --git a/boot/timer.c b/boot/timer.c
@@ -1,25 +0,0 @@
-#include "extern.h"
-#include "idt.h"
-#include "port.h"
-
-static void timer_callback(struct reg *);
-
-static uint32_t timer_ticks = 0;
-
-static void
-timer_callback(struct reg *r)
-{
- timer_ticks++;
- (void)r;
-}
-
-void
-timer_init(uint32_t hz)
-{
- uint32_t div = 1193180 / hz;
-
- int_add_handler(0, timer_callback);
- outb(P_TIMER_CMD, 0x36);
- outb(P_TIMER_DATA, (uint8_t)(div & 0xff));
- outb(P_TIMER_DATA, (uint8_t)((div >> 8) & 0xff));
-}
diff --git a/boot/tty.c b/boot/tty.c
@@ -1,120 +0,0 @@
-#include "io.h"
-#include "port.h"
-#include "tty.h"
-
-static struct tty_info tty;
-
-void
-tty_clear(void)
-{
- size_t y, x;
-
- tty_curs_enable(0x0e, 0x0f); /* Low cursor shape. */
- tty_curs_setpos(0, 0);
- tty.row = 0;
- tty.col = 0;
- tty.color = VGA_SET_COLOR(VGA_BLUE, VGA_WHITE);
- tty.buf = (uint16_t *)_VGA_MEM;
- for (x = 0; x < _VGA_COLS; x++)
- for (y = 0; y < _VGA_ROWS; y++)
- tty.buf[y * _VGA_COLS + x] = _PUTC(' ');
-}
-
-void
-tty_putc(char c)
-{
- switch (c) {
- case '\n':
- tty.row++;
- tty.col = 0;
- break;
- case '\b':
- tty.col--;
- tty.buf[tty.row * _VGA_COLS + tty.col] = _PUTC(' ');
- break;
- case '\r':
- tty.col = 0;
- break;
- case '\t':
- tty.col += 8;
- break;
- default:
- tty.buf[tty.row * _VGA_COLS + tty.col] = _PUTC(c);
- tty.col++;
- }
-
- if (tty.row >= _VGA_ROWS) {
- tty.row = 0;
- tty.col = 0;
- }
- if (tty.col >= _VGA_COLS) {
- tty.row++;
- tty.col = 0;
- }
- tty_curs_setpos(tty.col, tty.row);
-}
-
-void
-tty_write(const char *str)
-{
- while (*str != '\0')
- tty_putc(*str++);
-}
-
-/*
- * The arguments `start` and `end` define the shape of the cursor.
- * The lowest scaline value is 0x00 and the highest 0x0f. A cursor shape
- * with `start = 0x00` and `end = 0x0f` is effectively a block cursor.
- */
-void
-tty_curs_enable(uint8_t start, uint8_t end)
-{
- /* 0x0a: Low cursor shape */
- outb(P_CURS_CMD, 0x0a);
- outb(P_CURS_DATA, (inb(P_CURS_DATA) & 0xc0) | start);
- outb(P_CURS_CMD, 0x0b);
- outb(P_CURS_DATA, (inb(P_CURS_DATA) & 0xe0) | end);
-}
-
-void
-tty_curs_disable(void)
-{
- outb(P_CURS_CMD, 0x0a);
- /*
- * Bit representation:
- * 7 6 | 5 | 4 0
- * Unused | Disable | Shape
- */
- outb(P_CURS_DATA, 0x20);
-}
-
-/*
- * Returns the position in `y * _VGA_COLS + x` format.
- * x = pos % _VGA_COLS
- * y = pos / _VGA_COLS
- */
-uint16_t
-tty_curs_getpos(void)
-{
- uint16_t pos = 0;
-
- outb(P_CURS_CMD, 0x0e);
- pos |= (uint16_t)inb(P_CURS_DATA) << 8;
- outb(P_CURS_CMD, 0x0f);
- pos |= inb(P_CURS_DATA);
-
- return pos;
-}
-
-void
-tty_curs_setpos(int x, int y)
-{
- uint16_t pos = y * _VGA_COLS + x;
-
- /* Expect 8 highest bits */
- outb(P_CURS_CMD, 0x0e);
- outb(P_CURS_DATA, (uint8_t)((pos >> 8) & 0xff));
- /* Expect 8 lowest bits */
- outb(P_CURS_CMD, 0x0f);
- outb(P_CURS_DATA, (uint8_t)(pos & 0xff));
-}
diff --git a/boot/tty.h b/boot/tty.h
@@ -1,47 +0,0 @@
-#ifndef _KERNEL_TTY_H_
-#define _KERNEL_TTY_H_
-
-#include <stddef.h>
-
-#define _VGA_MEM 0xb8000;
-#define _VGA_COLS 80
-#define _VGA_ROWS 25
-#define _PUTC(c) (((uint16_t)tty.color << 8) | (c))
-
-#define VGA_SET_COLOR(fg, bg) ((fg) | (bg) << 4)
-
-struct tty_info {
- volatile uint16_t *buf;
- size_t row;
- size_t col;
- uint8_t color;
-};
-
-enum vga_color {
- VGA_BLACK = 0,
- VGA_BLUE,
- VGA_GREEN,
- VGA_CYAN,
- VGA_RED,
- VGA_MAGENTA,
- VGA_BROWN,
- VGA_LIGHT_GREY,
- VGA_DARK_GREY,
- VGA_LIGHT_BLUE,
- VGA_LIGHT_GREEN,
- VGA_LIGHT_CYAN,
- VGA_LIGHT_RED,
- VGA_LIGHT_MAGENTA,
- VGA_LIGHT_BROWN,
- VGA_WHITE,
-};
-
-void tty_clear(void);
-void tty_putc(char);
-void tty_write(const char *);
-void tty_curs_enable(uint8_t, uint8_t);
-void tty_curs_disable(void);
-void tty_curs_setpos(int, int);
-uint16_t tty_curs_getpos(void);
-
-#endif /* _KERNEL_TTY_H_ */
diff --git a/hier b/hier
@@ -1,11 +0,0 @@
-/bin/
- boot/
-/dev/
-/sys/
- include/
- dev/
- kernel/
- sys/
- lib/
- src/
-/usr
diff --git a/include/dev/kbd.h b/include/dev/kbd.h
@@ -0,0 +1,6 @@
+#ifndef _KERNEL_KBD_H_
+#define _KERNEL_KBD_H_
+
+void kbd_init(void);
+
+#endif /* _KERNEL_KBD_H_ */
diff --git a/boot/stdarg.h b/include/stdarg.h
diff --git a/include/sys/idt.h b/include/sys/idt.h
@@ -0,0 +1,99 @@
+#ifndef _KERNEL_IDT_H_
+#define _KERNEL_IDT_H_
+
+/* IA-32 */
+struct idt_gate {
+ uint16_t off_lo;
+ uint16_t sel;
+ uint8_t zero;
+ uint8_t flags;
+ uint16_t off_hi;
+} __attribute__((packed));
+
+/* This will be populated by `int_common_stub` in `int.asm`. */
+struct reg {
+ /* Will be popped last. */
+ uint32_t gs;
+ uint32_t fs;
+ uint32_t es;
+ uint32_t ds;
+ /* Pushed by `pusha`. */
+ uint32_t edi;
+ uint32_t esi;
+ uint32_t ebp;
+ uint32_t esp;
+ uint32_t ebx;
+ uint32_t edx;
+ uint32_t ecx;
+ uint32_t eax;
+ /* Interrupt info. Pushed by `push byte`. */
+ uint32_t intno;
+ uint32_t err;
+ /* Pushed by the CPU. */
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t eflags;
+ uint32_t uesp;
+ uint32_t ss;
+};
+
+/* Called by `kern_main`. */
+void idt_init(void);
+
+/* Called by drivers. */
+void int_handler(struct reg *);
+void int_add_handler(uint8_t, void (*)(struct reg *));
+
+/* The first 32 interrupts are reserved for exceptions. */
+void ex0(void);
+void ex1(void);
+void ex2(void);
+void ex3(void);
+void ex4(void);
+void ex5(void);
+void ex6(void);
+void ex7(void);
+void ex8(void);
+void ex9(void);
+void ex10(void);
+void ex11(void);
+void ex12(void);
+void ex13(void);
+void ex14(void);
+void ex15(void);
+void ex16(void);
+void ex17(void);
+void ex18(void);
+void ex19(void);
+void ex20(void);
+void ex21(void);
+void ex22(void);
+void ex23(void);
+void ex24(void);
+void ex25(void);
+void ex26(void);
+void ex27(void);
+void ex28(void);
+void ex29(void);
+void ex30(void);
+void ex31(void);
+
+/* IRQs */
+void irq0(void);
+void irq1(void);
+void irq2(void);
+void irq3(void);
+void irq4(void);
+void irq5(void);
+void irq6(void);
+void irq7(void);
+void irq8(void);
+void irq9(void);
+void irq10(void);
+void irq11(void);
+void irq12(void);
+void irq13(void);
+void irq14(void);
+void irq15(void);
+
+#endif /* _KERNEL_IDT_H_ */
diff --git a/include/sys/io.h b/include/sys/io.h
@@ -0,0 +1,69 @@
+#ifndef _KERNEL_IO_H_
+#define _KERNEL_IO_H_
+
+#include <stdint.h>
+
+static inline uint8_t
+inb(uint16_t port)
+{
+ uint8_t res;
+
+ __asm__ __volatile__ ("in %%dx, %%al" : "=a" (res) : "d" (port));
+ return (res);
+}
+
+static inline void
+outb(uint16_t port, uint8_t v)
+{
+ __asm__ __volatile__ ("out %%al, %%dx" : : "a" (v), "d" (port));
+}
+
+static inline uint16_t
+inw(uint16_t port)
+{
+ uint16_t res;
+
+ __asm__ __volatile__ ("in %%dx, %%ax" : "=a" (res) : "d" (port));
+ return (res);
+}
+
+static inline void
+outw(uint16_t port, uint16_t v)
+{
+ __asm__ __volatile__ ("out %%ax, %%dx" : : "a" (v), "d" (port));
+}
+
+static inline uint32_t
+inl(uint16_t port)
+{
+ uint32_t res;
+
+ __asm__ __volatile__ ("in %%dx, %%eax" : "=a" (res) : "d" (port));
+ return (res);
+}
+
+static inline void
+outl(uint16_t port, uint32_t v)
+{
+ __asm__ __volatile__ ("out %%eax, %%dx" : : "a" (v), "d" (port));
+}
+
+static inline void
+hlt(void)
+{
+ __asm__ __volatile__ ("hlt");
+}
+
+static inline void
+cli(void)
+{
+ __asm__ __volatile__ ("cli");
+}
+
+static inline void
+sti(void)
+{
+ __asm__ __volatile__ ("sti");
+}
+
+#endif /* _KERNEL_IO_H_ */
diff --git a/include/sys/libk.h b/include/sys/libk.h
@@ -0,0 +1,26 @@
+#ifndef _KERNEL_LIBK_H_
+#define _KERNEL_LIBK_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#include <sys/io.h>
+#include <sys/tty.h>
+
+#define BUFSIZ 1024
+
+#define ARRLEN(x) (sizeof(x) / sizeof((x)[0]))
+#define UNUSED(x) ((void)(x))
+#define sizeof_field(s, f) (sizeof(((t *)0)->f))
+
+void *memset(void *, int, size_t);
+void *memcpy(void *, const void *, size_t);
+size_t strlen(const char *);
+int strcmp(const char *, const char *);
+int vsprintf(char *, const char *, va_list);
+int sprintf(char *, const char *, ...);
+int printf(const char *, ...);
+void panic(const char *, ...);
+
+#endif /* _KERNEL_LIBK_H_ */
diff --git a/boot/port.h b/include/sys/port.h
diff --git a/boot/timer.h b/include/sys/timer.h
diff --git a/include/sys/tty.h b/include/sys/tty.h
@@ -0,0 +1,34 @@
+#ifndef _KERNEL_TTY_H_
+#define _KERNEL_TTY_H_
+
+#include <stddef.h>
+
+enum vga_color: uint8_t {
+ VGA_BLACK = 0,
+ VGA_BLUE,
+ VGA_GREEN,
+ VGA_CYAN,
+ VGA_RED,
+ VGA_MAGENTA,
+ VGA_BROWN,
+ VGA_LIGHT_GREY,
+ VGA_DARK_GREY,
+ VGA_LIGHT_BLUE,
+ VGA_LIGHT_GREEN,
+ VGA_LIGHT_CYAN,
+ VGA_LIGHT_RED,
+ VGA_LIGHT_MAGENTA,
+ VGA_LIGHT_BROWN,
+ VGA_WHITE,
+};
+
+void tty_clear(uint8_t, uint8_t);
+void tty_set_color(uint8_t, uint8_t);
+void tty_putc(char);
+void tty_write(const char *);
+void tty_curs_enable(uint8_t, uint8_t);
+void tty_curs_disable(void);
+void tty_curs_setpos(int, int);
+uint16_t tty_curs_getpos(void);
+
+#endif /* _KERNEL_TTY_H_ */
diff --git a/kern/Makefile b/kern/Makefile
@@ -0,0 +1,52 @@
+BIN = os.bin
+BINDIR = ../build
+
+CC = cc
+ASM = nasm
+LD = ld
+INCDIR = ../include
+CFLAGS = -g -m32 -nostdlib -ffreestanding -Wall -Wextra -std=c99 -O2 -I${INCDIR}
+LDFLAGS = -Ttext 0x1000 --oformat binary
+
+BOOTFILE = boot.asm
+BOOT_BIN = boot.bin
+KERNEL_BIN = kernel.bin
+SRC = *.c *.asm
+OBJ = kern_main.o \
+ libk.o \
+ tty.o \
+ idt.o \
+ int.o \
+ kbd.o \
+ timer.o
+
+all: options ${BIN}
+
+options:
+ @echo ${BIN} build options
+ @echo CC = ${CC}
+ @echo ASM = ${ASM}
+ @echo LD = ${LD}
+ @echo CFLAGS = ${CFLAGS}
+ @echo LDFLAGS = ${LDFLAGS}
+
+${BIN}: ${OBJ}
+ mkdir -p ${BINDIR}
+ ${ASM} -fbin ${BOOTFILE} -o ${BOOT_BIN}
+ ${LD} ${LDFLAGS} ${OBJ} -o ${KERNEL_BIN}
+ dd if=/dev/zero bs=1000000 count=1 >> ${KERNEL_BIN}
+ cat ${BOOT_BIN} ${KERNEL_BIN} > $@
+
+.c.o:
+ ${CC} -c ${CFLAGS} $<
+
+.asm.o:
+ ${ASM} -felf $<
+
+install: all
+ mv ${BIN} ${BINDIR}
+
+clean:
+ rm -rf *.bin *.o
+
+.PHONY: all options install clean
diff --git a/boot/boot.asm b/kern/boot.asm
diff --git a/kern/idt.c b/kern/idt.c
@@ -0,0 +1,162 @@
+#include <sys/libk.h>
+#include <sys/idt.h>
+#include <sys/port.h>
+
+#define N_INT 256
+#define KERN_CODESEG 0x08
+
+static void idt_set_gate(uint8_t, uint32_t);
+
+static struct idt_gate idt[N_INT];
+static void *isr[16] = {NULL};
+static const char *except[] = {
+ "Division By Zero Exception",
+ "Debug Exception",
+ "Non Maskable Interrupt Exception",
+ "Breakpoint Exception",
+ "Into Detected Overflow Exception",
+ "Out of Bounds Exception",
+ "Invalid Opcode Exception",
+ "No Coprocessor Exception",
+ "Double Fault Exception",
+ "Coprocessor Segment Overrun Exception",
+ "Bad TSS Exception",
+ "Segment Not Present Exception",
+ "Stack Fault Exception",
+ "General Protection Fault Exception",
+ "Page Fault Exception",
+ "Unknown Interrupt Exception",
+ "Coprocessor Fault Exception",
+ "Alignment Check Exception (486+)",
+ "Machine Check Exception (Pentium/586+)",
+ "Reserved Exception",
+ "Reserved Exception",
+ "Reserved Exception",
+ "Reserved Exception",
+ "Reserved Exception",
+ "Reserved Exception",
+ "Reserved Exception",
+ "Reserved Exception",
+ "Reserved Exception",
+ "Reserved Exception",
+ "Reserved Exception",
+ "Reserved Exception",
+ "Reserved Exception",
+};
+
+static void
+idt_set_gate(uint8_t n, uint32_t ptr)
+{
+ idt[n].off_lo = ptr & 0xffff;
+ idt[n].sel = KERN_CODESEG;
+ idt[n].zero = 0;
+ /* The gate is present and is running in kernel mode. */
+ idt[n].flags = 0x8e;
+ idt[n].off_hi = (ptr >> 16) & 0xffff;
+}
+
+void
+idt_init(void)
+{
+ struct idt_reg {
+ uint16_t limit; /* IDT size in bytes. */
+ uint32_t base; /* Points at `idt[0]`. */
+ } __attribute__((packed)) idtr;
+
+ (void)memset(&idt, 0, sizeof(idt));
+
+ /* Set up exception interrupts. */
+ idt_set_gate(0, (uint32_t)ex0);
+ idt_set_gate(1, (uint32_t)ex1);
+ idt_set_gate(2, (uint32_t)ex2);
+ idt_set_gate(3, (uint32_t)ex3);
+ idt_set_gate(4, (uint32_t)ex4);
+ idt_set_gate(5, (uint32_t)ex5);
+ idt_set_gate(6, (uint32_t)ex6);
+ idt_set_gate(7, (uint32_t)ex7);
+ idt_set_gate(8, (uint32_t)ex8);
+ idt_set_gate(9, (uint32_t)ex9);
+ idt_set_gate(10, (uint32_t)ex10);
+ idt_set_gate(11, (uint32_t)ex11);
+ idt_set_gate(12, (uint32_t)ex12);
+ idt_set_gate(13, (uint32_t)ex13);
+ idt_set_gate(14, (uint32_t)ex14);
+ idt_set_gate(15, (uint32_t)ex15);
+ idt_set_gate(16, (uint32_t)ex16);
+ idt_set_gate(17, (uint32_t)ex17);
+ idt_set_gate(18, (uint32_t)ex18);
+ idt_set_gate(19, (uint32_t)ex19);
+ idt_set_gate(20, (uint32_t)ex20);
+ idt_set_gate(21, (uint32_t)ex21);
+ idt_set_gate(22, (uint32_t)ex22);
+ idt_set_gate(23, (uint32_t)ex23);
+ idt_set_gate(24, (uint32_t)ex24);
+ idt_set_gate(25, (uint32_t)ex25);
+ idt_set_gate(26, (uint32_t)ex26);
+ idt_set_gate(27, (uint32_t)ex27);
+ idt_set_gate(28, (uint32_t)ex28);
+ idt_set_gate(29, (uint32_t)ex29);
+ idt_set_gate(30, (uint32_t)ex30);
+ idt_set_gate(31, (uint32_t)ex31);
+
+ /* Remap the PIC */
+ /* TODO: explain.*/
+ outb(P_PIC1_CMD, 0x11);
+ outb(P_PIC2_CMD, 0x11);
+ outb(P_PIC1_DATA, 0x20);
+ outb(P_PIC2_DATA, 0x28);
+ outb(P_PIC1_DATA, 0x04);
+ outb(P_PIC2_DATA, 0x02);
+ outb(P_PIC1_DATA, 0x01);
+ outb(P_PIC2_DATA, 0x01);
+ outb(P_PIC1_DATA, 0x00);
+ outb(P_PIC2_DATA, 0x00);
+
+ /* Set up IRQs */
+ idt_set_gate(32, (uint32_t)irq0);
+ idt_set_gate(33, (uint32_t)irq1);
+ idt_set_gate(34, (uint32_t)irq2);
+ idt_set_gate(35, (uint32_t)irq3);
+ idt_set_gate(36, (uint32_t)irq4);
+ idt_set_gate(37, (uint32_t)irq5);
+ idt_set_gate(38, (uint32_t)irq6);
+ idt_set_gate(39, (uint32_t)irq7);
+ idt_set_gate(40, (uint32_t)irq8);
+ idt_set_gate(41, (uint32_t)irq9);
+ idt_set_gate(42, (uint32_t)irq10);
+ idt_set_gate(43, (uint32_t)irq11);
+ idt_set_gate(44, (uint32_t)irq12);
+ idt_set_gate(45, (uint32_t)irq13);
+ idt_set_gate(46, (uint32_t)irq14);
+ idt_set_gate(47, (uint32_t)irq15);
+
+ /* https://wiki.osdev.org/Interrupt_Descriptor_Table#Location_and_Size */
+ idtr.base = (uint32_t)&idt;
+ idtr.limit = N_INT * sizeof(struct idt_gate) - 1;
+ __asm__ __volatile__ ("lidtl (%0)" : : "r" (&idtr));
+}
+
+void
+int_handler(struct reg *r)
+{
+ void (*handler)(struct reg *);
+
+ /*
+ * We'll call the handler only if the interrupt number is > 32,
+ * which means that we're dealing with an IRQ and not an exception.
+ */
+ if (r->intno >= 32 && (handler = isr[r->intno - 32]) != NULL)
+ handler(r);
+ /* Entries below index 32 in the IDT are exceptions, we need to hang. */
+ else if (r->intno < 32)
+ panic("%s: System halted...\n", except[r->intno]);
+ if (r->intno >= 40)
+ outb(P_PIC2_CMD, 0x20);
+ outb(P_PIC1_CMD, 0x20);
+}
+
+void
+int_add_handler(uint8_t intno, void (*handler)(struct reg *r))
+{
+ isr[intno] = handler;
+}
diff --git a/boot/int.asm b/kern/int.asm
diff --git a/kern/kbd.c b/kern/kbd.c
@@ -0,0 +1,64 @@
+#include <sys/libk.h>
+#include <sys/idt.h>
+#include <sys/port.h>
+
+#include <dev/kbd.h>
+
+static void kbd_callback(struct reg *);
+
+static unsigned char kbdus[128] = {
+ 0, /* Error */
+ 27, /* Escape */
+ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
+ '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p',
+ '[', ']', '\n',
+ 0, /* Control */
+ 'a', 's', 'd', 'f', 'g',
+ 'h', 'j', 'k', 'l', ';', '\'', '`',
+ 0, /* Left Shift */
+ '\\', 'z', 'x',
+ 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
+ 0, /* Right Shift */
+ '*',
+ 0, /* Alt */
+ ' ',
+ 0, /* Caps Lock */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F1-F10 */
+ 0, /* Num Lock */
+ 0, /* Scroll Lock */
+ 0, /* Home Key */
+ 0, /* Up Arrow */
+ 0, /* Page Up */
+ '-',
+ 0, /* Left Arrow */
+ 0,
+ 0, /* Right Arrow */
+ '+',
+ 0, /* End Key */
+ 0, /* Down Arrow */
+ 0, /* Page Down */
+ 0, /* Insert Key */
+ 0, /* Delete Key */
+ 0, 0, 0,
+ 0, 0, /* F11, F12 */
+ 0, /* The rest are undefined */
+};
+
+static void
+kbd_callback(struct reg *r)
+{
+ uint8_t sc;
+
+ if ((sc = inb(P_KBD)) & 0x80) {
+ } else {
+ tty_putc(kbdus[sc]);
+ }
+
+ UNUSED(r);
+}
+
+void
+kbd_init(void)
+{
+ int_add_handler(1, kbd_callback);
+}
diff --git a/kern/kern_main.c b/kern/kern_main.c
@@ -0,0 +1,29 @@
+#include <sys/libk.h>
+#include <sys/idt.h>
+#include <sys/io.h>
+#include <sys/timer.h>
+
+#include <dev/kbd.h>
+
+#define OK() ((void)printf("ok\n"))
+
+void
+kern_main(void)
+{
+ tty_clear(VGA_BLUE, VGA_WHITE);
+
+ printf("IDT... ");
+ idt_init();
+ OK();
+
+ printf("Timer... ");
+ timer_init(50);
+ OK();
+
+ printf("Keyboard... ");
+ kbd_init();
+ OK();
+
+ sti();
+ for (;;);
+}
diff --git a/kern/libk.c b/kern/libk.c
@@ -0,0 +1,150 @@
+#include <sys/libk.h>
+#include <sys/io.h>
+
+static char *
+itoa(int n, char *buf, int base)
+{
+ (void)n;
+ (void)base;
+
+ return (buf);
+}
+
+void *
+memset(void *dst, int v, size_t len)
+{
+ unsigned char *dst0;
+
+ dst0 = dst;
+ while (len--)
+ *dst0++ = v;
+
+ return (dst);
+}
+
+void *
+memcpy(void *dst, const void *src, size_t len)
+{
+ const unsigned char *src0;
+ unsigned char *dst0;
+
+ src0 = src;
+ dst0 = dst;
+
+ while (len--)
+ *dst0++ = *src0++;
+
+ return (dst);
+}
+
+size_t
+strlen(const char *str)
+{
+ const char *s;
+
+ for (s = str; *s != '\0'; s++)
+ ; /* nothing */
+
+ return (s - str);
+}
+
+int
+strcmp(const char *s1, const char *s2)
+{
+ while (*s1 == *s2++)
+ if (*s1++ == '\0')
+ return (0);
+
+ return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1));
+}
+
+int
+vsprintf(char *buf, const char *fmt, va_list args)
+{
+ char *str, *s;
+ int base, i, n;
+
+ for (str = buf; *fmt != '\0'; fmt++) {
+ base = 10;
+ if (*fmt != '%') {
+ *str++ = *fmt;
+ continue;
+ }
+ if (*fmt == '%') {
+ switch (*(++fmt)) {
+ case 'c':
+ *str++ = (unsigned char)va_arg(args, int);
+ continue;
+ case 's':
+ s = va_arg(args, char *);
+ n = strlen(s);
+ for (i = 0; i < n; i++)
+ *str++ = *s++;
+ continue;
+ case 'p':
+ continue;
+ case 'd': /* FALLTHROUGH */
+ case 'i':
+ n = va_arg(args, int);
+ break;
+ case 'u':
+ n = va_arg(args, unsigned int);
+ break;
+ case 'o':
+ base = 8;
+ break;
+ case 'X':
+ base = 16;
+ break;
+ case '%':
+ *str++ = '%';
+ continue;
+ }
+ str = itoa(n, str, base);
+ }
+ }
+ *str = '\0';
+
+ return (str - buf);
+}
+
+int
+sprintf(char *buf, const char *fmt, ...)
+{
+ va_list args;
+ int n;
+
+ va_start(args, fmt);
+ n = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ return (n);
+}
+
+int
+printf(const char *fmt, ...)
+{
+ char buf[BUFSIZ];
+ va_list args;
+ int n;
+
+ va_start(args, fmt);
+ n = vsprintf(buf, fmt, args);
+ va_end(args);
+ tty_write(buf);
+
+ return (n);
+}
+
+void
+panic(const char *fmt, ...)
+{
+ va_list args;
+
+ cli();
+ printf("Kernel panic!\n");
+ va_start(args, fmt);
+ printf(fmt, args);
+ va_end(args);
+ hlt();
+}
diff --git a/kern/timer.c b/kern/timer.c
@@ -0,0 +1,26 @@
+#include <sys/libk.h>
+#include <sys/idt.h>
+#include <sys/port.h>
+
+static void timer_callback(struct reg *);
+
+static uint32_t timer_ticks = 0;
+
+static void
+timer_callback(struct reg *r)
+{
+ timer_ticks++;
+
+ UNUSED(r);
+}
+
+void
+timer_init(uint32_t hz)
+{
+ uint32_t div = 1193180 / hz;
+
+ int_add_handler(0, timer_callback);
+ outb(P_TIMER_CMD, 0x36);
+ outb(P_TIMER_DATA, (uint8_t)(div & 0xff));
+ outb(P_TIMER_DATA, (uint8_t)((div >> 8) & 0xff));
+}
diff --git a/kern/tty.c b/kern/tty.c
@@ -0,0 +1,139 @@
+#include <sys/libk.h>
+#include <sys/io.h>
+#include <sys/port.h>
+#include <sys/tty.h>
+
+#define VGA_MEM 0xb8000;
+#define VGA_COLS 80
+#define VGA_ROWS 25
+#define TTY_PUTC(c) (((uint16_t)tty.color << 8) | (c))
+
+struct tty_info {
+ volatile uint16_t *buf;
+ size_t row;
+ size_t col;
+ uint8_t color;
+};
+
+static struct tty_info tty;
+
+void
+tty_clear(uint8_t fg, uint8_t bg)
+{
+ size_t y, x;
+
+ tty_curs_enable(0x0e, 0x0f); /* Low cursor shape. */
+ tty_curs_setpos(0, 0);
+ tty.row = 0;
+ tty.col = 0;
+ tty.buf = (uint16_t *)VGA_MEM;
+ tty_set_color(fg, bg);
+ for (x = 0; x < VGA_COLS; x++)
+ for (y = 0; y < VGA_ROWS; y++)
+ tty.buf[y * VGA_COLS + x] = TTY_PUTC(' ');
+}
+
+void
+tty_set_color(uint8_t fg, uint8_t bg)
+{
+ tty.color = fg | (bg << 4);
+}
+
+void
+tty_putc(char c)
+{
+ switch (c) {
+ case '\n':
+ tty.row++;
+ tty.col = 0;
+ break;
+ case '\b':
+ tty.col--;
+ tty.buf[tty.row * VGA_COLS + tty.col] = TTY_PUTC(' ');
+ break;
+ case '\r':
+ tty.col = 0;
+ break;
+ case '\t':
+ tty.col += 8;
+ break;
+ default:
+ tty.buf[tty.row * VGA_COLS + tty.col] = TTY_PUTC(c);
+ tty.col++;
+ }
+
+ if (tty.row >= VGA_ROWS) {
+ tty.row = 0;
+ tty.col = 0;
+ }
+ if (tty.col >= VGA_COLS) {
+ tty.row++;
+ tty.col = 0;
+ }
+ tty_curs_setpos(tty.col, tty.row);
+}
+
+void
+tty_write(const char *str)
+{
+ while (*str != '\0')
+ tty_putc(*str++);
+}
+
+/*
+ * The arguments `start` and `end` define the shape of the cursor.
+ * The lowest scaline value is 0x00 and the highest 0x0f. A cursor shape
+ * with `start = 0x00` and `end = 0x0f` is effectively a block cursor.
+ */
+void
+tty_curs_enable(uint8_t start, uint8_t end)
+{
+ /* 0x0a: Low cursor shape */
+ outb(P_CURS_CMD, 0x0a);
+ outb(P_CURS_DATA, (inb(P_CURS_DATA) & 0xc0) | start);
+ outb(P_CURS_CMD, 0x0b);
+ outb(P_CURS_DATA, (inb(P_CURS_DATA) & 0xe0) | end);
+}
+
+void
+tty_curs_disable(void)
+{
+ outb(P_CURS_CMD, 0x0a);
+ /*
+ * Bit representation:
+ * 7 6 | 5 | 4 0
+ * Unused | Disable | Shape
+ */
+ outb(P_CURS_DATA, 0x20);
+}
+
+/*
+ * Returns the position in `y * VGA_COLS + x` format.
+ * x = pos % VGA_COLS
+ * y = pos / VGA_COLS
+ */
+uint16_t
+tty_curs_getpos(void)
+{
+ uint16_t pos = 0;
+
+ outb(P_CURS_CMD, 0x0e);
+ pos |= (uint16_t)inb(P_CURS_DATA) << 8;
+ outb(P_CURS_CMD, 0x0f);
+ pos |= inb(P_CURS_DATA);
+
+ return (pos);
+}
+
+void
+tty_curs_setpos(int x, int y)
+{
+ uint16_t pos = y * VGA_COLS + x;
+
+ /* Expect 8 highest bits */
+ outb(P_CURS_CMD, 0x0e);
+ outb(P_CURS_DATA, (uint8_t)((pos >> 8) & 0xff));
+ /* Expect 8 lowest bits */
+ outb(P_CURS_CMD, 0x0f);
+ outb(P_CURS_DATA, (uint8_t)(pos & 0xff));
+}
diff --git a/boot/unused/lm.asm b/kern/unused/lm.asm