diff --git a/tools/bdf-to-stm/.gitignore b/tools/bdf-to-stm/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..2f7896d1d1365eafb0da03d9fe456fac81408487
--- /dev/null
+++ b/tools/bdf-to-stm/.gitignore
@@ -0,0 +1 @@
+target/
diff --git a/tools/bdf-to-stm/Cargo.lock b/tools/bdf-to-stm/Cargo.lock
new file mode 100644
index 0000000000000000000000000000000000000000..cf1054284a404e73b3aea01463c514d0f9790f04
--- /dev/null
+++ b/tools/bdf-to-stm/Cargo.lock
@@ -0,0 +1,50 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "bdf"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bit-set 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "bdf-to-stm"
+version = "0.1.0"
+dependencies = [
+ "bdf 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "bit-set"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bit-vec 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "either"
+version = "1.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "itertools"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[metadata]
+"checksum bdf 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a28b986e043da7dc2a39dc58940571c4da585cb2e222843c3e261fbe64435eb9"
+"checksum bit-set 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "84527c7b0452f22545cc010e72d366a435561d2b28b978035550b3778c4d428d"
+"checksum bit-vec 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a4523a10839ffae575fb08aa3423026c8cb4687eef43952afb956229d4f246f7"
+"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
+"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
diff --git a/tools/bdf-to-stm/Cargo.toml b/tools/bdf-to-stm/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..88dcb36f0cef95c26e3b84087f725d13d2a29f67
--- /dev/null
+++ b/tools/bdf-to-stm/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "bdf-to-stm"
+version = "0.1.0"
+authors = ["meh"]
+edition = "2018"
+
+[dependencies]
+bdf = "0.5.3"
+itertools = "0.8.0"
diff --git a/tools/bdf-to-stm/README.md b/tools/bdf-to-stm/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..6430921252072e43f162e006c0a0959f4e62e202
--- /dev/null
+++ b/tools/bdf-to-stm/README.md
@@ -0,0 +1,6 @@
+Usage
+=====
+
+```shell-session
+$ cargo run -- gohufont < gohufont-11.bdf
+```
diff --git a/tools/bdf-to-stm/src/main.rs b/tools/bdf-to-stm/src/main.rs
new file mode 100644
index 0000000000000000000000000000000000000000..59592b499ecc9224ab42ed8ea32e4f13550ec2e4
--- /dev/null
+++ b/tools/bdf-to-stm/src/main.rs
@@ -0,0 +1,85 @@
+use bdf;
+use itertools::Itertools;
+use std::{env, error::Error, io, iter};
+
+const CHARSET: &[char] = &[
+    ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2',
+    '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E',
+    'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+    'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
+    'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',
+];
+
+fn main() -> Result<(), Box<dyn Error>> {
+    let name = env::args().nth(1).expect("no font name provided");
+    let font = bdf::read(io::stdin())?;
+
+    println!("#include \"fonts.h\"");
+    println!("const uint8_t {name}_Table[] =", name = name);
+    println!("{{");
+
+    for (&ch, glyph) in CHARSET
+        .iter()
+        .map(|ch| (ch, font.glyphs().get(ch).unwrap()))
+    {
+        println!();
+        println!(
+            "\t// @{codepoint} {ch:?} ({width} pixels wide)",
+            codepoint = ch as usize,
+            ch = ch,
+            width = glyph.width()
+        );
+
+        for y in 0..glyph.height() {
+            print!("\t");
+
+            let mut draw = String::new();
+
+            for bits in &iter::repeat(false)
+                .take(glyph.bounds().x as usize)
+                .chain((0..glyph.width()).map(|x| glyph.map().get(x, y)))
+                .chunks(8)
+            {
+                let mut bits = bits.collect::<Vec<_>>();
+                if bits.len() < 8 {
+                    for _ in 0..8 - bits.len() {
+                        bits.push(false);
+                    }
+                }
+
+                let byte = bits
+                    .into_iter()
+                    .rev()
+                    .enumerate()
+                    .fold(0, |acc, (i, b)| acc | ((b as u8) << i));
+
+                draw.push_str(&format!("{1:00$b}", glyph.width() as usize, byte));
+
+                print!("0x{:02X}, ", byte);
+            }
+
+            println!("//{}", draw.replace("1", "#").replace("0", " "));
+        }
+
+        // Print the right amount of empty lines.
+        for _ in 0..font.bounds().y.abs() + glyph.bounds().y {
+            for _ in 0..(glyph.width() + glyph.bounds().x as u32) / 8 {
+                print!("0x{:02X}, ", 0);
+            }
+
+            println!();
+        }
+    }
+
+    println!("}};");
+    println!();
+
+    println!(
+        "sFONT {name} = {{ {name}_Table, {width}, {height} }};",
+        name = name,
+        width = font.bounds().width,
+        height = font.bounds().height
+    );
+
+    Ok(())
+}