diff --git a/build.rs b/build.rs
index b53fae90545914afdc65e1e5228ea312a921c36b..14da3e829c4834fad8f147141b105cf86362677b 100644
--- a/build.rs
+++ b/build.rs
@@ -1,4 +1,12 @@
-use std::{env::var, error::Error};
+use std::{
+    env::var,
+    error::Error,
+    fs::{self, File, OpenOptions},
+    io::Read,
+    io::Write,
+    path::PathBuf,
+    process::Command,
+};
 
 //
 
@@ -35,5 +43,30 @@ fn main() -> Result<(), Box<dyn Error>> {
         panic!();
     };
 
+    let unifont_path = "target/hyperion/unifont.bmp";
+    let read_unifont = || {
+        let mut file = OpenOptions::new()
+            .read(true)
+            .create(false)
+            .write(false)
+            .open(unifont_path)?;
+
+        let mut buf = Vec::new();
+        file.read_to_end(buf)?;
+        Ok::<_, Box<dyn Error>>(buf)
+    };
+
+    let unifont = if let Ok(file) = read_unifont() {
+        file
+    } else {
+        Command::new("wget")
+            .arg("http://unifoundry.com/pub/unifont/unifont-15.0.01/unifont-15.0.01.bmp")
+            .args(["-O", unifont_path])
+            .spawn()
+            .unwrap();
+
+        read_unifont().unwrap()
+    };
+
     Ok(())
 }
diff --git a/src/arch/x86_64/limine/framebuffer.rs b/src/arch/x86_64/limine/framebuffer.rs
index 70df236452e161ef9752b0b8ec75fda6f3ec0799..8b530409a05c47d932e4f95f6f91853d547e7fd5 100644
--- a/src/arch/x86_64/limine/framebuffer.rs
+++ b/src/arch/x86_64/limine/framebuffer.rs
@@ -2,9 +2,9 @@ use crate::{
     println,
     video::framebuffer::{Framebuffer, FBO},
 };
-use core::{ops::Deref, slice};
-use limine::{LimineFramebuffer, LimineFramebufferRequest, LimineFramebufferResponse};
-use spin::{Lazy, Mutex, MutexGuard};
+use core::slice;
+use limine::LimineFramebufferRequest;
+use spin::Mutex;
 
 //
 
diff --git a/src/arch/x86_64/limine/term.rs b/src/arch/x86_64/limine/term.rs
index 97446b77877f01611012ee0c0c732d57570d3181..784002656cc7066eafa551b714fe94d0b0eb6041 100644
--- a/src/arch/x86_64/limine/term.rs
+++ b/src/arch/x86_64/limine/term.rs
@@ -19,7 +19,7 @@ unsafe impl Send for Writer {}
 
 impl Write for Writer {
     fn write_str(&mut self, s: &str) -> fmt::Result {
-        let mut write = self.0.write().ok_or(fmt::Error)?;
+        let write = self.0.write().ok_or(fmt::Error)?;
 
         for term in self.0.terminals() {
             write(term, s);
diff --git a/src/main.rs b/src/main.rs
index 4d81da639e8ab8a3e2880d86b5ab32669c9c8087..420484ff04f71be5f93156f477b40a21e7c9e02c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -53,6 +53,8 @@ fn kernel_main() -> ! {
         fbo.fill(40, 40, 40, 40, Color::RED);
         fbo.fill(50, 50, 60, 40, Color::GREEN);
         fbo.fill(5, 15, 80, 20, Color::BLUE);
+
+        fbo.put_bytes(100, 100, b"ABABABAB\0", Color::WHITE, Color::BLACK);
     }
 
     #[cfg(test)]
@@ -61,6 +63,7 @@ fn kernel_main() -> ! {
     arch::done();
 }
 
+#[allow(unused)]
 fn stack_overflow(n: usize) {
     if n == 0 {
         return;
diff --git a/src/qemu.rs b/src/qemu.rs
index cb30c3ac3d39da6d8a0db2a7558427464456a34e..f65d38d41b0e4fc451a085ac5b830987b22b8a83 100644
--- a/src/qemu.rs
+++ b/src/qemu.rs
@@ -1,7 +1,4 @@
-use core::{
-    fmt::{Arguments, Write},
-    sync::atomic::AtomicUsize,
-};
+use core::fmt::{Arguments, Write};
 use spin::{Lazy, Mutex};
 use uart_16550::SerialPort;
 
@@ -16,6 +13,9 @@ pub fn _print(args: Arguments) {
 }
 
 /// Unlocks the COM1 writer IF it is locked by this exact thread
+///
+/// SAFETY: this is unsafe unless called from the same thread, this is intended for double fault
+/// handling
 pub unsafe fn unlock() {
     // TODO: SMP
     // if COM1_LOCKER.load(Ordering::SeqCst) != crate::THREAD {
@@ -27,7 +27,7 @@ pub unsafe fn unlock() {
 
 //
 
-static COM1_LOCKER: AtomicUsize = AtomicUsize::new(0);
+// static COM1_LOCKER: AtomicUsize = AtomicUsize::new(0);
 static COM1: Lazy<Mutex<SerialPort>> = Lazy::new(|| {
     let mut port = unsafe { SerialPort::new(0x3f8) };
     port.init();
diff --git a/src/video/font.rs b/src/video/font.rs
index 9d16b727050a01d25c5bdc96fd11ed8de200441d..25a61372b9a04af797f3dbcad7d62e173344272e 100644
--- a/src/video/font.rs
+++ b/src/video/font.rs
@@ -3,14 +3,13 @@
 //     bitmap: [u8; 16],
 // }
 
+#[allow(clippy::unusual_byte_groupings)]
 pub static FONT: [[u8; 16]; 256] = {
-    let mut font = [[0u8; 16]; 256];
-
-    font[b'a' as usize] = [
+    let default = [
         0b_11111111,
         0b_11111111,
         0b_11000011,
-        0b_11000011,
+        0b_11000011, //
         0b_11000011,
         0b_11000011,
         0b_11000011,
@@ -25,5 +24,44 @@ pub static FONT: [[u8; 16]; 256] = {
         0b_11111111,
     ];
 
+    let mut font = [default; 256];
+
+    font[b'A' as usize] = [
+        0b_00000000,
+        0b_00000000,
+        0b_00000000, //
+        0b_01111110,
+        0b_11111111,
+        0b_11100111,
+        0b_11000011,
+        0b_11111111,
+        0b_11111111,
+        0b_11000011,
+        0b_11000011,
+        0b_11000011,
+        0b_11000011,
+        0b_00000000, //
+        0b_00000000,
+        0b_00000000,
+    ];
+    font[b'B' as usize] = [
+        0b_00000000,
+        0b_00000000,
+        0b_00000000, //
+        0b_11111110,
+        0b_11111111,
+        0b_11000111,
+        0b_11000111,
+        0b_11111110,
+        0b_11111110,
+        0b_11000111,
+        0b_11000111,
+        0b_11111111,
+        0b_11111110,
+        0b_00000000, //
+        0b_00000000,
+        0b_00000000,
+    ];
+
     font
 };
diff --git a/src/video/framebuffer.rs b/src/video/framebuffer.rs
index 1f96c5cadbba64854b544a3b2d419e2c876948dc..e26a9cbbc41e0b056fe51497b94632e425b69dc5 100644
--- a/src/video/framebuffer.rs
+++ b/src/video/framebuffer.rs
@@ -1,7 +1,5 @@
-use core::{
-    fmt,
-    ops::{Deref, DerefMut},
-};
+use super::font::FONT;
+use core::fmt;
 use spin::{Mutex, Once};
 
 //
@@ -40,10 +38,31 @@ impl Framebuffer {
             }
         }
     }
+
+    pub fn put_byte(&mut self, x: usize, y: usize, ch: u8, fg: Color, bg: Color) {
+        let map = FONT[ch as usize];
+
+        for (yd, row) in map.into_iter().enumerate() {
+            for xd in 0..8 {
+                self.set(
+                    x + xd,
+                    y + yd,
+                    if (row & 1 << (7 - xd)) != 0 { fg } else { bg },
+                );
+            }
+        }
+    }
+
+    pub fn put_bytes(&mut self, x: usize, y: usize, s: &[u8], fg: Color, bg: Color) {
+        for (offs, ch) in s.iter().enumerate() {
+            self.put_byte(x + 12 * offs, y, *ch, fg, bg)
+        }
+    }
 }
 
 impl Color {
     pub const WHITE: Color = Color::new(0xff, 0xff, 0xff);
+    pub const BLACK: Color = Color::new(0x00, 0x00, 0x00);
 
     pub const RED: Color = Color::new(0xff, 0x00, 0x00);
     pub const GREEN: Color = Color::new(0x00, 0xff, 0x00);