Bare-Metal Snake Game Bootloader
x86 boot sector that boots straight into a Snake game, written in 16-bit real-mode NASM assembly.
5 min read
A real x86 boot sector that boots straight into a playable Snake game: no OS, no kernel, no C runtime. Written entirely in 16-bit real-mode NASM assembly and run under QEMU’s x86 emulation.
Scope decision: implemented in pure 16-bit real-mode NASM assembly, not assembly+C: a freestanding 16-bit real-mode C cross-compiler (OpenWatcom, bcc, etc.) isn’t installed and isn’t practical to set up reliably here, and hand-written assembly boot-sector games are the standard approach for this category of project anyway.
What it does
Boots a 2-stage bootloader from a floppy image, sets VGA text mode, and runs a real Snake game loop: the snake moves each tick in the last-pressed direction (WASD or arrow keys, polled non-blocking), grows when it eats food, and the game ends on wall or self-collision. Progress is logged over the serial port (COM1) at each checkpoint so the whole run can be verified headlessly, without a human watching a GUI window.
Build
./build.sh
This runs, equivalently:
nasm -f bin boot.asm -o boot.bin # stage 1: 512-byte MBR
nasm -f bin stage2.asm -o stage2.bin # stage 2: the game
truncate -s 4096 stage2.bin # pad stage2 to 8 sectors
cat boot.bin stage2.bin > snake.img # 512 + 4096 = 4608-byte floppy image
Run
Interactive (real display, real keyboard: WASD or arrow keys):
qemu-system-i386 -fda snake.img -boot a
Headless proof (what was used to verify this build: captures serial output to a file and force-quits after a few seconds since the game otherwise runs forever):
qemu-system-i386 -fda snake.img -serial file:serial_out.txt -display none -no-reboot &
QEMU_PID=$!
sleep 6
kill $QEMU_PID
cat serial_out.txt
(macOS’s stock shell doesn’t ship a timeout command, hence the
background-and-kill instead of timeout 6 qemu-system-i386 ...: same
effect.)
Captured serial log (actual output from a verification run)
stage1: booted, loading stage2...
stage1: loaded stage2 OK, jumping
stage2: entered stage2, setting video mode
stage2: entered game loop
stage2: food eaten count: 1
stage2: game over, score=1
This proves the full chain executed for real: stage 1’s INT 13h disk read
of stage 2 succeeded, stage 2 set video mode and entered the game loop, the
snake’s movement/collision code correctly detected reaching the food cell
(grew, incremented score), and then correctly detected the wall collision
that ends the game. The initial snake and food are placed so this sequence
happens automatically even with no keyboard input, which is what makes the
proof reproducible headlessly: the same collision/eating code path runs
identically when a human is pressing keys.
Architecture
2-stage boot, since a real playable Snake game doesn’t fit in a 512-byte
MBR alongside the required 0xAA55 signature:
- Stage 1 (
boot.asm, 512 bytes, loaded by BIOS at0x7C00): sets up segments/stack, logs a serial checkpoint, usesINT 13h AH=02hto read 8 sectors (stage 2) from CHS sector 2 onward into0x0000:0x8000, logs a second checkpoint, jumps to0x8000. - Stage 2 (
stage2.asm, assembled to run at0x8000, padded to 8 sectors / 4096 bytes): the game itself.
Memory layout:
| Address | Contents |
|---|---|
0x7C00 |
Stage 1 (MBR), loaded by BIOS |
0x8000 |
Stage 2, loaded by stage 1 |
0xB800:0000 |
VGA text-mode framebuffer (written directly) |
0x0040:006C |
BIOS tick counter, used for frame timing |
I/O port 0x3F8 |
COM1 UART, used for the serial debug log |
Video mode: VGA text mode 0x03 (80x25, 16-colour), set via INT 10h.
The snake is drawn as solid block characters (0xDB) written directly to
the 0xB800 framebuffer. Food is *. Border is #.
Game loop: each tick, polls INT 16h AH=01h for a non-blocking
keystroke (WASD or arrow scan codes, ignoring 180-degree reversals), waits
for the BIOS tick counter (0x0040:006C) to advance by 2 ticks (~110ms) for
consistent pacing, computes the new head position, checks wall/self
collision, and either grows (food eaten) or shifts the body array
(normal move).
Files
boot.asm: stage 1 bootloaderstage2.asm: stage 2, the Snake gamebuild.sh: assembles both stages and builds the floppy imagesnake.img: built floppy image (run./build.shto produce it)serial_out.txt: sample captured serial log from a verification run
Simplified vs full spec
- Pure assembly, not assembly+C: no freestanding 16-bit C cross-compiler was available/practical to set up. Add when a real-mode-capable C toolchain (e.g. OpenWatcom) is installed and worth the added build complexity.
- Text-mode block characters, not pixel graphics: VGA text mode 0x03 is
simpler and fully legitimate for Snake. Add when mode 0x13 (320x200,
0xA000framebuffer) pixel rendering is wanted for a smoother look. - Serial-log proof by default, not a screenshot: the serial log is the
required, automatable, headless proof. Add when a visual check is
wanted: use QEMU’s monitor
screendumpcommand to save a PPM/PNG alongside the serial capture. - Fixed-size 60-segment snake array, no dynamic growth beyond that: more than enough for a text-mode board of this size. Add when MAX_LEN needs raising for a larger board.
- Food placement doesn’t avoid the snake’s own body: a simple 16-bit LCG picks a cell without checking for overlap. Add when long play sessions make an occasional food-on-snake spawn noticeable.
Support
Submit issues, questions, or bug reports to the GitHub issue tracker: https://github.com/ADM1SH/bootloader-snake/issues
Roadmap
- Done: Core architecture and baseline implementation.
- Done: Functional verification and test coverage.
- To do: Add sound effect via PC speaker port (0x61)
- To do: Implement two-player split controls
Contributing
Contributions are welcome.
- Fork the repository.
- Create a feature branch:
git checkout -b feature/improvement. - Commit your changes:
git commit -m "feat: enhance functionality". - Push to the branch:
git push origin feature/improvement. - Open a Pull Request.
Authors and Acknowledgment
- Adam Anwar (ADM1SH) - Lead architect and developer.
- Authored by Adam Anwar. Low-level BIOS interrupt reference: Ralph Brown’s Interrupt List.
License
Licensed under the MIT License. See LICENSE for details.
Project Status
Completed bare-metal demo. Boots and runs under QEMU.