Files
mckero 61344e486e Minesweeper: verify its logic on the host, and document it in both languages
apps/minesweeper/host_test.c includes the app source with a fake app_api_t, so the real state machine runs on a PC: every string it draws is recorded and the lit pixels are counted. Measured -- a reveal/flag/new-game/digit/quit script returns normally, draws the title, the mine count and lights 24 pixels; a script that blindly reveals 85 cells reaches a terminal state eleven times, draws BOOM, and MENU starts a new game after it (terminal at record 356, title again at 4180). The win path is the one branch blind play does not reach.

Both READMEs now say what is verified (compiles clean under gcc -Wall -Wextra -Werror against upstream's real app_api.h, which is what caught the API's true member names and the absence of left/right keys; the logic runs on the host) and what is not (never built for ARM, never run on the radio -- no toolchain and no Docker here).
2026-10-01 22:31:22 +08:00

3.4 KiB
Raw Permalink Blame History

扫雷 —— F4HWN Labs 版的叠加应用

我们自己的应用 ✓:9×9 扫雷 ✓,跑在 App/apps/app_overlay.h 预留的 4 KiB 叠加区里 ✓。 它是按上游的 App/apps/app_api.h 写的 ✓、用上游的 app.ld 链接 ✓ —— 这两个上游文件没有随本仓库分发 ✗ (它们来自 armel/uv-k1-k5v3-firmware-custom,Apache-2.0 ✓), 所以把这个目录放进 App/apps/minesweeper/ 与它们并列 ✓,或把 -I 指向一份副本 ✓。

为什么它长这样

约束 后果
这台电台没有左右键 ✓(只有 UP、DOWN、MENU、EXIT、STAR、F、0-9 ✓) 光标用 UP/DOWN 走 ✓,数字先选行再选列 ✓:3 5 = 第 3 行第 5 列 ✓
4 KiB(text+rodata+data+bss 合计 ✓) 不用查表 ✓、不用浮点 ✓、不链 libc ✓;邻雷数现算 ✓,每格一个 bit ✓
81 格放不进 16 位掩码 ✓ 三个 9 字节位数组 ✓,按 cell >> 3、cell & 7 寻址 ✓ —— 早先的 uint16_t 版本能编过但在第 15 格之后是错的 ✗
常驻的像素函数不做边界检查 ✓ put()/invert() 自己裁剪 ✓
freestanding 里没有 rand() ✓ 一个小 LCG ✓;雷在第一次翻开之后才布 ✓,并保证首翻周围 3×3 无雷 ✓

按键:UP/DOWN 移动 ✓、1-9 先选行再选列 ✓、MENU 翻开 ✓、F 插旗 ✓、STAR 重开 ✓、EXIT 退出 ✓。 角上的 M 是剩余雷数 ✓,A1 是光标位置 ✓。

构建

arm-none-eabi-gcc -mcpu=cortex-m0plus -mthumb -Os -std=gnu11 -ffreestanding \
    -nostdlib -nostartfiles -T app.ld -Wl,--defsym,APP_VMA=0x20000280 \
    -o minesweeper.elf minesweeper_app.c
arm-none-eabi-objcopy -O binary minesweeper.elf minesweeper.bin
pack_app.py minesweeper.bin Minesweeper.app --name Minesweeper --ver 1.0 \
    --vma 0x20000280 --api-min 1      # pack_app.py 在 App/apps/ 下

build.sh 做的就是这些 ✓,需要 PATH 上有 Arm GNU Toolchain ✓。

然后从网页安装 ✓:Overlay apps → 选一个槽 → 选 Minesweeper.app → Install → Ask the radio 应当回 Minesweeper ✓。在电台上按 F 再按 7、再按 MENU 运行 ✓。

验证到什么程度(host_test.c 可复现)

host_test.c 把应用源码直接包进来 ✓、给它一个假的 app_api_t ✓,在 PC 上真的跑它的状态机 ✓ (记录它每一次画出的字符串、统计点亮的像素 ✓):

gcc -std=gnu11 -O1 -Wall -Wextra -Werror -I.. -o host_test host_test.c && ./host_test

实测输出 ✓:脚本一(翻开/插旗/重开/数字选格/退出)正常返回 ✓、标题与雷数都画了 ✓、有像素点亮 ✓; 脚本二(盲翻 85 格)到达终局 11 次 ✓、BOOM 已绘制 ✓、终局后按 MENU 回到新局 ✓。

已经验证 ✓:源码在 gcc -Wall -Wextra -Werror 下干净通过 ✓(这一步抓出了 API 的真实成员名 ✓ api->fb ✓、print_tiny(s, x, y, statusbar, fill) 五个参数 ✓,以及 APP_KEY_LEFT/APP_KEY_RIGHT 根本不存在 ✓);游戏逻辑在 PC 上跑通 ✓。

没有验证 ✗:从未为 ARM 编译过 ✓、也没在电台上跑过 ✓ —— 写它的机器上既没有 arm-none-eabi-gcc 也没有 Docker ✓。第一次构建与第一次上机就是真正的评审 ✓。