Skip to content

Build and install GameQ on Q

Install Git, command-line developer tools (xcode-select --install if needed), and Docker Desktop or a working Docker-compatible local engine. Start Docker. The build downloads a Debian-based toolchain once and caches it locally. It contains ARM GCC 12.2, newlib, Python signing dependencies, and RGBDS 1.0.3. RGBDS is built from a checksum-verified source archive. No Homebrew installs or global Python package changes happen during a build.

Terminal window
git clone --recursive --branch main git@github.com:tcashel/GameQ.git
cd GameQ
make pokered
make firmware
make sd SD=/Volumes/GAMEQ

main contains the tested 0.1.7GQ baseline. The operator confirmed walking, CAPS fast-forward and save/reload on the Q. See hardware-test.md for the installed artifact and remaining checks.

A normal non-recursive clone also works: the scripts initialize the pinned dependencies they actually need. This avoids downloading the unrelated ESP32, Pico, and other MCU SDKs in the full recursive dependency tree.

  • build/roms/pokemon-red.gb: 1,048,576 bytes; canonical SHA-1 ea9bcae617fdf159b045185467ae58b2e4a48b9a.
  • build/roms/gameq-hub.gb: the walkable library, built with make hub from the pinned Red source plus the GameQ hub overlays. The full 1 MiB ROM is compressed losslessly into firmware; the room does not need an SD copy.
  • build/roms/gameq-hub.build.json: hashes tying the room ROM and symbols to their source inputs. Firmware builds rebuild the room; SD staging verifies this manifest before changing the card.
  • build/firmware/gameq-q1.dfu: the file to install.
  • build/firmware/builtin-hub.json: hashes and sizes binding the compressed flash asset to the complete hub ROM and its source manifest.
  • build/firmware/gameq-q1.bin: signed application payload.
  • build/firmware/gameq-q1.elf and .map: symbols and memory accounting.
  • build/firmware/memory-budget.json: linked heap/core sizes; builds require at least 256 KiB of MicroPython heap before runtime allocations.
  • build/firmware/verification.json: DFU structure and signature result.
  • build/firmware/source-info.json: commit, source pins, and input hashes.
  • build/firmware/gameq-stack-usage.txt: ARM GCC stack estimates for the native module.
  • build/firmware/SHA256SUMS: exported binary hashes.

make sd builds Red and the hub, stages Red, then copies an already-built gameq-q1.dfu to the card root. It preserves existing saves. Firmware builds embed the room; staging does not copy it to SD. Legacy gameq-hub.gb files on the card are ignored by the library, as are hidden macOS metadata files. Eject the card after staging. Use the Q’s slot A for bring-up and recovery.

The build uses a source snapshot in Docker BuildKit and exports artifacts. It does not need USB, privileged containers, or access to a physical Q. The upstream checkouts remain available for inspection; build-time overlays are applied inside the snapshot. make prepare is an optional inspection helper that applies those overlays to the local Coldcard checkout.

The DFU contains one application element at 0x08020000, targets Q only, and uses Coinkite’s published developer key 0. The immutable bootloader is not part of the image. Header length, flash budget, reset vector, DFU CRC, and ECDSA signature are checked. The high-water/downgrade-protection flag is not set. The build also checks that the native module and frozen boot shell were linked. The resident hub is part of that signed application, not a separate internal filesystem install. Build checks round-trip the compressed asset back to the exact ROM. The lean frozen runtime removes unused wallet menus and retains hardware support plus the authenticated GameQ recovery updater.

The signing timestamp defaults to the GameQ commit timestamp via SOURCE_DATE_EPOCH. Signing uses deterministic ECDSA. To deliberately select a newer build timestamp, set SOURCE_DATE_EPOCH to a Unix timestamp and keep that value with the build record. Generated firmware/ROMs are ignored by Git. Use GAMEQ_NO_CACHE=1 make firmware to force a fresh build execution when comparing two checkouts. Identical source, toolchain, and timestamp should produce identical DFU bytes; this does not claim bit-identical output from arbitrarily different system packages or compiler versions.

The generic MicroPython build message about installing mboot is inapplicable to the stock Q: use the packaged GameQ DFU through its existing update UI. Do not install MicroPython’s intermediate unsigned firmware.dfu.

  1. Build and stage the files above on a FAT32 MicroSD card.
  2. On the currently installed stock Q firmware, select Advanced/Tools → Upgrade Firmware → From MicroSD, and choose gameq-q1.dfu.
  3. Keep power connected throughout the update. Accept the immutable developer/custom-firmware warning and its forced delay.
  4. Follow the hardware checks in hardware-test.md.

The menu path follows Coinkite’s firmware-update instructions.

The marker must name the version currently installed on the Q. For a Q running 0.1.6GQ and updating to 0.1.7GQ, place this text in GameQ/system/recovery on the card:

for-version:0.1.6GQ
  1. Eject the card, insert it in slot A, and reboot with USB power connected.
  2. In GameQ recovery, use Left/Right to choose gameq-q1.dfu, then Enter to load and verify it.
  3. Enter the existing main PIN if requested, then confirm Install this new firmware?. Keep power connected through the developer warning and update.
  4. After installing 0.1.7GQ, the older marker no longer matches and the resident room opens automatically. Follow hardware-test.md.

The updater retains main-PIN authentication when a PIN is configured and bootloader signature verification. The operator successfully installed 0.1.7GQ through this updater; its Cancel return still needs explicit physical acceptance. For a later update from 0.1.7GQ, use for-version:0.1.7GQ.

If the installed version is 0.1.4GQ or earlier, use its version in the marker. Those versions enter stock startup: enter the existing PIN and select Advanced/Tools → Upgrade Firmware → From MicroSD.

Create an empty /GameQ/system/recovery file on the card and reboot. This marker is checked before importing the emulator and requests recovery on every version until removed. Through 0.1.4GQ it opens stock startup; from 0.1.5GQ it opens GameQ’s dedicated updater. Canceling the new updater opens the resident room; remove the empty marker to boot directly into the room next time.

From 0.1.2GQ, a for-version: marker selects recovery only while its named version is installed. Older GameQ builds treat any marker as recovery, so use for-version:0.1.1GQ when updating 0.1.1GQ.

This is experimental application firmware: an SD/LCD/PSRAM fault can leave it unresponsive. A correctly signed application that crashes does not automatically trigger the bootloader’s corrupt-image recovery. The marker helps when startup reaches the SD check; an earlier failure may still require hardware recovery. The immutable bootloader is preserved, but do not assume every application fault is recoverable from its normal SD screen.

Terminal window
make test # shell tests and host C adapter; real ROM if built
make firmware # ARM build, linked-module and signature checks

make clean removes exported firmware and ROM files. It preserves source edits and does not run git reset --hard or clean the upstream repositories.

The host adapter runs the actual canonical ROM with AddressSanitizer and UndefinedBehaviorSanitizer. It reaches the title and accepts input into the new-game introduction. This checks emulator behavior, not the Q’s physical SPI, keyboard, PSRAM timing, or SD power-loss behavior.

The package copies an existing verified firmware build; it does not rebuild or flash the Q. It includes the canonical Red ROM, an offline illustrated guide, checksums, source notices and update markers. It never includes saves or diagnostic files and does not put a recovery marker in the ready-to-copy GameQ folder.

After building firmware and the documentation screenshots:

Terminal window
python3 -m venv build/package-env
build/package-env/bin/python -m pip install ecdsa==0.19.1
build/package-env/bin/python scripts/package-release.py \
--upgrade-from 0.1.5GQ --upgrade-from 0.1.6GQ

Outputs are build/release-packages/GameQ-0.1.7GQ-starter.zip and its .sha256 file when packaging 0.1.7GQ. --firmware-dir, --rom and --output-dir can point to preserved release artifacts. The script verifies the developer signature, clean-source build record and canonical ROM before packaging. It refuses a version-scoped marker matching the packaged version, which would keep opening recovery after a same-version install. The separate unknown-version marker is blank; its guide explicitly requires removing it after updating.

Review README-FIRST.html, check package hashes and extract into a temporary folder before publishing. The same inputs produce the same ZIP bytes. Starter packages are distributed through the private repository’s release assets; generated ROMs, DFUs and ZIPs stay outside Git history.