Initial commit
diff --git a/src/vga.c b/src/vga.c
new file mode 100644
index 0000000..1b10833
--- /dev/null
+++ b/src/vga.c
@@ -0,0 +1,111 @@
+#include "vga.h"
+#include "mem.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--;
+		break;
+	case '\t':
+		cursor_x = (cursor_x + 8) & ~ 7;
+		break;
+	case '\n':
+		cursor_y++;
+	case '\r':
+		cursor_x = 0;
+		break;
+	default:
+		cursor_x++;
+		fb[cursor_y * 80 + cursor_x] = c | (color << 8);
+	}
+
+	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)
+{
+	
+}
+
+static void vga_put_nibble(uchar n)
+{
+	if (n <= 9)
+		vga_put('0' + n);
+	else
+		vga_put('A' + n - 10);
+}
+
+void vga_putx(uint x)
+{
+	for (uint mask = 0xFF000000, shift = 24; mask; mask >>= 8, shift -= 8)
+	{
+		uchar byte = (x & mask) >> shift;
+
+		vga_put_nibble((byte & 0xf0) >> 8);
+		vga_put_nibble(byte & 0x0f);
+	}
+}