1. Work
  2. Bare-Metal Snake Game Bootloader
  3. Docs

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

Source on GitHub

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 at 0x7C00): sets up segments/stack, logs a serial checkpoint, uses INT 13h AH=02h to read 8 sectors (stage 2) from CHS sector 2 onward into 0x0000:0x8000, logs a second checkpoint, jumps to 0x8000.
  • Stage 2 (stage2.asm, assembled to run at 0x8000, 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 bootloader
  • stage2.asm: stage 2, the Snake game
  • build.sh: assembles both stages and builds the floppy image
  • snake.img: built floppy image (run ./build.sh to 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, 0xA000 framebuffer) 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 screendump command 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.

  1. Fork the repository.
  2. Create a feature branch: git checkout -b feature/improvement.
  3. Commit your changes: git commit -m "feat: enhance functionality".
  4. Push to the branch: git push origin feature/improvement.
  5. 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.

Back to top