totp-gba
May 14, 2026 · View on GitHub
Native Game Boy Advance TOTP (RFC 6238) authenticator. Same protocol as Google Authenticator — add accounts, get rotating 6-digit codes — but running on real ARM7TDMI hardware off a flashcart.
Sibling repo to totp-gb, which
targets the original DMG / GBC. The crypto core (SHA1 / HMAC / Base32 /
TOTP) is shared verbatim; this project adds GBA-specific platform glue
(libtonc text engine, EWRAM-mapped SRAM, hardware RTC via Seiko S-3511A
with software-VBlank fallback, 240×160 layout, OAM sprite cursor).
Screens
![]() | ![]() | ![]() |
| Main list with live code + countdown bar | View account with bar urgency colors | Settings with theme picker |
![]() | ![]() | ![]() |
| Same screen, Solarized theme | Add account char dial | Set time with field cursor |
Full gallery + reproduction notes: docs/screens/README.md.
Try it
Pre-built ROMs are checked into artifacts/:
| File | Target | Notes |
|---|---|---|
totp-gba.gba | GBA / GBA SP / GBA Micro / DS / DS Lite / DSi (slot-2) | Native ARM7 build, ~36 KB |
totp-gba-test.gba | Same | Debug build that auto-seeds a Test account on every boot |
totp-gba-test.gbc | GBA, GBA SP, original DS slot-2 | Byte-identical copy of the GBC build for hardware backward-compat testing |
Load totp-gba.gba in mGBA or any GBA emulator, or flash it to a
slot-2 cart. First boot drops you into a Set Time screen (UTC). After
that you land on the live list — Start adds an account, A
views, B re-syncs the clock, SELECT opens settings.
Build from source
Requires devkitPro with the gba-dev package (includes devkitARM,
libgba, libtonc): https://github.com/devkitPro/installer/releases
.\build.bat # build totp-gba.gba and copy to artifacts\
.\build.bat test # build totp-gba-test.gba (auto-seeded for testing)
.\build.bat clean # nuke build/ + outputs
The wrapper bypasses PowerShell's WSL routing of make. On Linux/macOS
or in CI, just make / make test.
Features
- HMAC-SHA1 / TOTP per RFC 6238 — pure C from
totp-gb, ARM7-clean - Persistent storage for up to 8 accounts in cart SRAM at
0x0E000000 - Hardware RTC support — bit-banged Seiko S-3511A driver over the
cart-edge GPIO at
0x080000C4(the same chip the Pokémon Ruby/Sapphire carts use). Auto-detected at boot with conservative fail-closed probing; falls back to software VBlank counting on carts without an RTC chip. Settings screen shows which backend is active. - Software RTC fallback — VBlank-counted (60 Hz), heartbeat-saved every 30 s so persistence is never more than one window stale.
- Per-frame screen state caching — TOTP only re-runs when the 30 s window flips or the user scrolls the list
- Twelve color themes — Phosphor green, Amber CRT, Pip-Boy, Solarized, Cool blue, Hot pink, Game Boy DMG, Game Boy Pocket gray, Atari 2600, GB Camera sepia, Magenta dusk, Ice. Settings screen cycles them; choice persists in SRAM.
- Sprite cursor — OAM-driven bobbing arrow on selectable rows (Settings, Set Time), independent of the BG-tile text layer.
- Sound effects — APU click / confirm / error / window-flip beeps on PSG channels 1+4. Mutable from settings.
- Color-coded urgency countdown — bar transitions phosphor → amber → red as the 30 s window winds down; final 5 s show one '!' per second.
- Auto-scrolling long account names — selected row's name ticker-scrolls if it overflows the 16-char display window.
- Animated boot splash ("TOTP-GBA / Authenticator" with a marquee).
- mGBA self-test on boot — 4 known-answer vectors logged via the
mGBA debug-log register; visible in mGBA's Log window without any
user interaction. The C code also writes a probe byte to EWRAM
0x02000000for headless verifier polling.
Testing
Two layers, like the sibling project:
- Boot self-test — runs at every boot under mGBA. Logs PASS/FAIL
for
JBSWY3DPEHPK3PXPat four hardcoded epochs:[INFO] === TOTP-GBA self-test (Hello! secret) === [INFO] epoch=0 got=282760 expected=282760 PASS [INFO] epoch=1234567890 got=742275 expected=742275 PASS [INFO] epoch=1778088090 got=283711 expected=283711 PASS [INFO] epoch=1778088141 got=113232 expected=113232 PASS [INFO] Self-test result: 4/4 PASS - Headless verifier — same self-test, but the C code also writes
0x55(PASS) or0xAA(FAIL) to EWRAM0x02000000after running.tests/verify_test_rom.luapolls that cell and writes the verdict totests/result.txt;tests/run_headless.shwraps mgba-sdl + xvfb-run + a timeout for CI use.
mgba --script tests/verify_test_rom.lua artifacts/totp-gba.gba
# or for CI:
./tests/run_headless.sh
CI runs on every push (see .github/workflows/build.yml).
Layout
source/ C sources
main.c boot + self-test + dispatch
clock.c dual-backend RTC (hardware S-3511 / software VBlank)
gba_rtc.c Seiko S-3511A bit-banger over cart GPIO
storage.c SRAM I/O + Account CRUD
datetime.c epoch <-> Y/M/D
audio.c PSG SFX (click/confirm/error/window-flip)
ui_common.c shared text/palette helpers + theme apply
ui_main.c main list screen + theme table
ui_view.c view/delete account
ui_add.c two-step add flow
ui_charpicker.c text-entry dial
ui_timeset.c modal time setter
ui_settings.c palette / sound / RTC backend display
ui_cursor.c OAM sprite cursor (bobbing arrow)
mgba_log.c mGBA debug-log register wrapper
sha1.c hmac.c base32.c crypto core (shared with totp-gb)
totp.c
include/ C headers
artifacts/ Prebuilt ROM (committed)
tests/ Headless verifier
.github/workflows/ CI
Makefile devkitARM template + libtonc
build.bat Windows wrapper
Acknowledgments
- devkitPro / devkitARM — ARM toolchain
- libtonc — GBA helper library
- mGBA — accurate emulator + debug interface
- mcp-mgba — MCP server used to drive mGBA from Claude during development
License
Released under the MIT License.





