From b9291c22a6f2bd22a20fdf52f2b40249c2ebc17d Mon Sep 17 00:00:00 2001 From: QIU SHENGMING Date: Fri, 2 Oct 2026 15:28:52 +0800 Subject: [PATCH] Rounds 91-92: the framebuffer is page-major, and the game draws Round 91 put the count on the glass instead of in a bar: eleven pixels of n on row 0 and a marker at (0,0). The result was ink 52, marker 0, all bits 0, n = 0 -- draw() leaves the framebuffer empty, and the app's own marker did not appear either. That ruled out the bar's width and pointed at api->fb itself. Reading app_api.h:71 rather than reasoning: 'The framebuffer is the resident gFrameBuffer[FRAME_LINES][128]'. gFrameBuffer is 1024 bytes, eight pages of 128, so fb's first index is the PAGE and the pixel within it is a bit: A->fb[y >> 3][x] |= 1u << (y & 7). The app had been writing fb[y][x] with y up to 63, eight times past the end, and the round 89 change from bit-packed to one byte per pixel moved it from wrong to differently wrong. The frame now arrives and the ASCII reader makes it readable: the game's header, drawn by print_tiny at y=1, in a box from columns 42 to 84. Rows 7 and below are empty and that is correct -- every cell of a fresh game is closed and unflagged, so the 81-cell loop has nothing to paint. Two habits: when a contradictory measurement appears, make the next one unarguable, and when a type is in doubt read the header that defines it -- the answer was two lines above the typedef. Next: press MENU inside the game and watch the field appear. --- AGENTS.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ AGENTS.zh-CN.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 21c86cb..9a8d8fe 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3055,3 +3055,47 @@ wrote minutes ago needs the same scepticism as one written rounds ago. Next measurement, and it is designed to be unarguable: have the app write the count itself into a few bytes of the framebuffer as bits -- 11 bits of n, in known positions -- and read them straight out of the panel hex. No bar, no width, no decode: the number is on the glass or it is not. + +**Rounds 91-92: the framebuffer is page-major, the game draws, and the page now says so.** + +Round 91 settled the contradiction by putting the number on the glass instead of in a bar: the app writes +the framebuffer's non-zero byte count as eleven pixels on row 0 and a marker pixel at (0,0), then blits. + + after MENU ink 52, marker pixel 0, all eleven bits 0, n = 0 + +n = 0 -- draw() leaves the framebuffer empty -- and the app's own marker did not appear either. That ruled out +the bar's width as the explanation and pointed at api->fb itself, so the next thing was to read the API's own +documentation rather than reason about it. app_api.h:71 says it in one line: + + /* The framebuffer is the resident gFrameBuffer[FRAME_LINES][128]; the app draws + * into it and calls a blit_* to push it to the LCD. */ + typedef uint8_t (*app_fb_t)[128]; + +gFrameBuffer is 1024 bytes, which is eight pages of 128 -- so fb's first index is the PAGE, not the pixel row, +and the pixel within a page is a bit: + + A->fb[y >> 3][x] |= (uint8_t)(1u << (y & 7)); + +The app had been writing fb[y][x] with y up to 63, eight times past the end of the buffer, and the round 89 +"fix" from bit-packed to one byte per pixel moved it from wrong to differently wrong. Both helpers are now +page-major and the arithmetic is the API's own. + +The frame then arrives, and the ASCII reader makes it readable for the first time -- this is the game's header, +drawn by print_tiny at y=1: + + 0|...........................................#########################################...... + 1|..........................................##...#.#.#.#.#.#.#.#.######.##..##..###..##..... + 2|..........................................##.###.#.#.#.#.#.#...#####.#.#.#.#.#.#.####..... + 6|...........................................#########################################...... + +Rows 7 and below are empty, and that is correct rather than a fault: every cell of a fresh game is closed and +unflagged, so draw()'s 81-cell loop has nothing to paint. What is on the glass is the title, the counter and the +cursor, which is exactly the opening frame of the game. + +Two habits from this round, both already in the file. When a contradictory measurement appears, make the next +one unarguable -- eleven pixels of a number cannot be misread as a width. And when a type is in doubt, read the +header that defines it and uses it: the answer was in a comment two lines above the typedef, and rounds 89 to 91 +were spent inferring what the compiler could have been told. + +Next: press MENU inside the running game and watch the field appear, which is the last thing standing between +this and playable. diff --git a/AGENTS.zh-CN.md b/AGENTS.zh-CN.md index 49d2538..4b50816 100644 --- a/AGENTS.zh-CN.md +++ b/AGENTS.zh-CN.md @@ -2728,3 +2728,48 @@ boot 485,随后 ink 43,第 2 行读到第 42..84 列、26 个亮 —— 那 **下一次测量,且它被设计成无可争辩** ✓:**让应用把那个数本身以位的形式写进帧缓冲的几个字节里** ✓ —— **`n` 的 11 个位,放在已知的位置上** ✓ —— **然后直接从面板的十六进制里读回来** ✓。**没有竖条、没有宽度、没有解码:那个数要么在玻璃上,要么不在** ✓。 + +**第 91–92 轮:帧缓冲是「页为主」的,游戏在画,而页面第一次说明了这一点。** + +**第 91 轮用「把数字放到玻璃上」而不是「竖条」来终结那个矛盾** ✓:**应用把帧缓冲的非零字节数写成第 0 行上的十一个像素 ✓,并在 (0,0) 写一个标记像素 ✓,然后 blit** ✓。 + +``` +MENU 之后 ink 52,标记像素 0,十一个位全 0,n = 0 +``` + +**`n = 0` —— `draw()` 留下的帧缓冲是空的 ✓ —— 而应用自己的标记也没出现** ✗。 +**这就排除了「竖条宽度」这个解释 ✓,并把矛头指向 `api->fb` 本身 ✓ —— 所以下一步不是推理,而是去读 API 自己的文档** ✓。 +**`app_api.h:71` 一行就说清了** ✓: + +``` +/* The framebuffer is the resident gFrameBuffer[FRAME_LINES][128]; the app draws + * into it and calls a blit_* to push it to the LCD. */ +typedef uint8_t (*app_fb_t)[128]; +``` + +**`gFrameBuffer` 是 1024 字节 = 8 个页 × 128 ✓ —— 所以 `fb` 的第一个下标是「页」而不是「像素行」✓, +页内的像素是一个位** ✓: + +``` +A->fb[y >> 3][x] |= (uint8_t)(1u << (y & 7)); +``` + +**应用此前一直写 `fb[y][x]`、`y` 到 63 ✓ —— 越出缓冲末尾八倍** ✗;**而第 89 轮那次「从按位打包改成一字节一像素」的「修法」,只是把它从错的一种改成了错的另一种** ✗✓。 +**两个辅助函数现在都是页为主 ✓,算术就是 API 自己的** ✓。 + +**画面随之出现 ✓,而 ASCII 读法第一次让它可读** ✓ —— **下面是 `print_tiny` 在 y=1 画的表头** ✓: + +``` + 0|...........................................#########################################...... + 1|..........................................##...#.#.#.#.#.#.#.#.######.##..##..###..##..... + 2|..........................................##.###.#.#.#.#.#.#...#####.#.#.#.#.#.#.####..... + 6|...........................................#########################################...... +``` + +**第 7 行往下是空的 ✓,而这是对的、不是故障** ✓:**新开一局的每个格子都未翻开、未插旗 ✓,`draw()` 的 81 格循环本来就无可画** ✓。 +**玻璃上的是标题、计数器与光标 ✓ —— 那正是游戏的开局画面** ✓✓。 + +**这一轮留下两条习惯,本文件其实都已有别的说法** ✓。**当出现互相矛盾的测量时,让**下一次**测量无可争辩** ✓ —— **一个数的十一个像素不可能被误读成宽度** ✓。 +**而当类型存疑时,去读定义并使用它的那个头文件** ✓:**答案就在 typedef 上方两行的注释里 ✓,而第 89 至 91 轮花在了推测编译器本可以告知的事情上** ✓✓。 + +**下一步:在运行中的游戏里按 MENU,看雷区长出来** ✓ —— **那是挡在「可玩」前面的最后一件东西** ✓✓。