⟳ Update (2026-07-16): this ceiling was broken — and one premise below was wrong⟳ 更新(2026-07-16):這個天花板被打破了 —— 而下面有一個前提是錯的
This article stands as the investigation as it was, but its verdict has since been overturned, and honesty requires flagging it at the top. One of the three "limits" in §4 — "B · the 74LS373 octal latch is on the board, not in either die's netlist" — is false. That chip is modelled: a full 96-transistor switch-level module (u2), wired into the video address path and depended on by every correctly-rendered frame. Once that false premise was removed, ALERead turned out to be a plain three-dot timing bug, and it now passes — verified three independent ways. The full story, and the three-move fix, are in the sequel: Breaking the ceiling →
這篇文章保留為當時的調查原貌,但它的判決此後已被推翻,而誠實要求在最上面標註出來。§4 那三個「極限」之一 —— 「B · 74LS373 八位閂鎖在板上、不在任一晶粒的網表裡」 —— 是錯的。那顆晶片有被模:一個完整的 96 電晶體開關級模組(u2),接進視訊位址路徑,每一格正常渲染的畫面都依賴它。一旦拆掉那個錯誤前提,ALERead 原來只是一個平凡的三 dot 時序 bug,而它現在通過了 —— 三個獨立方式驗證。完整故事與三招修法在續集:打破天花板 →
1 · 140 of 141, and one that would not fall1 · 141 顆過了 140,剩一顆打不下來
The AccuracyCoin hardware suite has 141 tests whose expected values are verified on real consoles. Our switch-level engine — running the 2A03 and 2C02 transistor by transistor — passed 140. The last one, ALERead, is a reproduction of a famous effect from Damian Yerrick's boing2k7.nes: a single, precisely-timed LDA $2007 that corrupts a background tile fetch. This article is the full autopsy of why that one test became the sharpest boundary the whole project ever hit — and the good news hiding inside a negative result.
AccuracyCoin 硬體測試套件有 141 顆,期望值全在真機上驗證過。我們的開關級引擎 —— 一顆一顆電晶體地跑 2A03 與 2C02 —— 過了 140 顆。最後一顆 ALERead,重現的是 Damian Yerrick 的 boing2k7.nes 裡一個著名效應:一個抓準時機的 LDA $2007 汙染了一個背景 tile 的 fetch。這篇文章是完整的驗屍報告:為什麼這一顆測試成了整個專案撞過最鋒利的邊界 —— 以及一個負面結果裡藏著的好消息。
2 · What boing2k7 actually does2 · boing2k7 到底在做什麼
To save pins, the PPU multiplexes its low eight VRAM address pins with the data pins. So every VRAM access takes two cycles: on the first, the full address is output and the low eight bits are latched into an external 74LS373 octal latch by pulsing the ALE (Address Latch Enable) line; on the second, the PPU outputs only the upper six bits, the octal latch supplies the low eight, and the data is read back on the low pins. The NESdev Wiki states the failure mode precisely:
為了省腳位,PPU 把低八位 VRAM 位址腳和資料腳多工共用。所以每次 VRAM 存取要兩個週期:第一個週期輸出完整位址、脈衝 ALE(位址閂鎖致能)把低八位存進外部的 74LS373 八位閂鎖;第二個週期 PPU 只輸出高六位,八位閂鎖提供低八位,資料在低腳被讀回。NESdev Wiki 把這個失效模式講得一清二楚:
"Reads come from an address composed of the latched low 8 bits and the current high 6 bits. If rendering is toggled, these can become desynced, producing a hybrid address sourced from two different addresses and reading from an address the PPU itself never actually outputted."
「讀取來自一個由『被閂鎖的低八位』與『當前高六位』組成的位址。若渲染被切換,兩者可能失去同步,產生一個由兩個不同位址混成的 hybrid address,從一個 PPU 自己從未真正輸出過的位址讀取。」
boing2k7 arranges exactly that "hybrid address". A LDA $2007 read, cycle-counted onto a specific dot, leaves the octal latch holding a stale $FF (from a prior $2FFF attribute fetch). The next background low-plane fetch therefore reads from $0FFF instead of its real address — pulling in an opaque pixel run where the tile should be transparent, which a sprite-zero hit can detect. The test asks: does your emulator produce that phantom pixel? Real hardware does. A behavioural emulator (AprNes/TriCNES) does. Our switch-level engine did not.
boing2k7 布置的正是這個「hybrid address」。一個精算週期、瞄準特定 dot 的 LDA $2007 讀取,讓八位閂鎖持有一個過期的 $FF(來自前一次 $2FFF 屬性 fetch)。於是下一次背景低位面 fetch 從 $0FFF 讀取、而非它真正的位址 —— 在本該透明的 tile 位置拉進一串不透明像素,一個 sprite-zero hit 就能偵測到。測試問:你的模擬器產得出那個幻影像素嗎?真機會。行為層模擬器(AprNes/TriCNES)會。我們的開關級引擎不會。
3 · Three walls, one after another3 · 三道牆,一道接一道
Six forensic campaigns and six independent expert consults later, the reasons formed a stack. Each looks fixable until you hit the next.
六場取證戰役、六輪獨立專家諮詢之後,原因疊成了一落。每一道看起來都可修,直到你撞上下一道。
Wall 1 — the downstream shim over-fires牆一 — 下游 shim 過度開火
The obvious fix is to model the missing octal latch: when a $2007 read lands during rendering, force the next background fetch's low byte to the held value. This passed ALERead. But it also fired on the $2007-stress tests — which read $2007 constantly — and corrupted fetches that should have been clean, turning two passing tests into failures. The corruption is data-dependent: a time-window heuristic cannot tell boing2k7's one deliberate collision from a stress test's thousands of benign reads. (The tempting "only fire when the held byte is $FF" gate is 100% overfitting — the latch holds whatever the previous fetch left there; $FF is a coincidence of this test's tile data.)
最直覺的修法是把那顆缺失的八位閂鎖補上:當 $2007 讀取落在渲染期,把下一個背景 fetch 的低位元組強制成閂鎖持有值。這過了 ALERead。但它也在 $2007 壓力測試上開火 —— 那些測試瘋狂讀 $2007 —— 汙染了本該乾淨的 fetch,把兩顆原本會過的測試變成失敗。這個汙染是資料相依的:時間窗啟發式分不出 boing2k7 那一次刻意的對撞,和壓力測試裡數千次無害的讀取。(那個誘人的「只在持有值是 $FF 時開火」條件是 100% 過擬合 —— 閂鎖持有的是前一次 fetch 留下的任何值,$FF 只是這顆測試 tile 資料的巧合。)
Wall 2 — the upstream fix is architecturally impossible牆二 — 上游修法在架構上不可能
If a downstream patch is too blunt, fix it upstream: the real problem is that our CPU→PPU cross-chip timing is idealized to zero delay, so the $2007 read's effect lands about one CPU cycle (three PPU dots) early — the octal latch samples at the wrong dot. Delay the access by 24 half-cycles and the phase corrects itself. Except: a binary switch-level engine resolves each wire group by a fixed priority — a path to ground wins, then VDD, then external drives. That makes it trivial to force a wire low but, when the netlist is actively driving a wire low, essentially impossible to force it high. You cannot delay a netlist-driven trigger (re-asserting it high fails), and you cannot replay the access later either — 24 half-cycles on, the CPU has moved to its next instruction and is strongly driving the shared bus with a new opcode, so the stale address/data can't be injected. A behavioural model just writes bus = value; a switch-level engine cannot.
如果下游 patch 太粗,就往上游修:真正的問題是我們的 CPU→PPU 跨晶片時序被理想化成零延遲,所以 $2007 讀取的效應早了約一個 CPU 週期(三個 PPU dot)—— 八位閂鎖在錯的 dot 取樣。把存取延遲 24 個半週期,相位就自己修正了。只是:二值開關級引擎用固定優先級解每個線群 —— 通往地的路徑贏,其次 VDD,再其次外部驅動。這讓「把線壓低」很容易,但當網表正主動驅動一條線為低時,「把它拉高」基本上不可能。你無法延遲一個網表驅動的觸發信號(重新拉高會失敗),也無法稍後重播那個存取 —— 24 個半週期後,CPU 已經走到下一條指令、正用新的 opcode 強力驅動共用匯流排,過期的位址/資料注不進去。行為層模型只要寫 bus = value;開關級引擎做不到。
Wall 3 — it was never one test牆三 — 它從來不是一顆測試
The deepest turn: that same "cross-chip effect lands three dots early" is also the root cause of two tests we had fixed — the dot-339 sprite-counter reset and the even-odd frame timing. All three are one phenomenon: propagation latency (the pins, wires and synchronizers between the two chips) quantized to zero by a settle-to-quiescence model. The difference is only in what the early signal lands on. dot-339 and even-odd land on internal PPU logic nodes that have a clean downstream point to intercept, so a surgical delay fixes them. ALERead's lands on the external board 74LS373, through a data-dependent corruption — and neither the downstream nor the upstream intercept is reachable, as Walls 1 and 2 just showed.
最深的轉折:那個「跨晶片效應早三個 dot」同時是另外兩顆我們已經修好的測試的根因 —— dot-339 的 sprite counter 重設、以及 even-odd 的畫格時序。三顆是同一個現象:傳播延遲(兩顆晶片之間的腳位、走線、同步器)被 settle-到-靜止模型量化成零。差別只在那個提早的信號打在什麼上面。dot-339 和 even-odd 打在PPU 內部邏輯節點上、有乾淨的下游點可攔截,外科手術式的延遲就修好了。ALERead 打在板上的外部 74LS373、透過一個資料相依的汙染 —— 而下游和上游的攔截都碰不到,牆一和牆二剛剛證明了。
4 · The ceiling, stated exactly4 · 天花板,精確陳述
ALERead is the only test in all 141 that sits at the intersection of three limits at once:
ALERead 是全部 141 顆裡唯一同時坐落在三個極限交集上的:
| Limit極限 | Here在這顆 |
|---|---|
| A4 propagation latency傳播延遲 | the cross-chip $2007 effect lands 3 dots early — the settle model has no time axis to hold the delay跨晶片 $2007 效應早 3 dot —— settle 模型沒有時間軸能承載這個延遲 |
| B unmodelled board part未建模的板級零件 ✗ | u2) and working; see the sequel.u2)且運作正常;見續集。 |
| binary engine二值引擎 | GND>VDD>drive priority can neither force a driven wire high nor inject stale bus data — so no shim can reach the effectGND>VDD>drive 優先級既不能把被驅動的線拉高、也不能注入過期匯流排資料 —— 所以沒有 shim 碰得到這個效應 |
This is why it is not "we haven't found the shim yet". dot-339 and even-odd prove the A4 ceiling can sometimes be shimmed; ALERead proves it sometimes cannot. That distinction — a limit that is genuinely unreachable, not merely unsolved — is the real result. Six consults with a frontier model, cross-checked against the NESdev Wiki and two independent silicon-derived emulators, all converged on the same verdict: a pure binary switch-level engine, lacking a virtual tri-state bus model, cannot reproduce this. 140 / 141 is the honest, falsifiable ceiling.
這就是為什麼它不是「我們還沒找到 shim」。dot-339 和 even-odd 證明 A4 天花板有時能被 shim;ALERead 證明它有時不能。這個區別 —— 一個真正碰不到、而非只是還沒解的極限 —— 才是真正的結果。六輪與前沿模型的諮詢,交叉比對 NESdev Wiki 與兩個獨立的、從矽推導的模擬器,全部收束到同一個判決:一個缺少虛擬三態匯流排模型的純二值開關級引擎,重現不了這個。140 / 141 是誠實、可證偽的天花板。
5 · The good news: not absolute, just switch-level5 · 好消息:不是絕對,只是開關級
The ceiling is pure switch-level, not absolute. The behavioural emulators that pass ALERead do it by modelling exactly the parts our die-only netlist omits — an explicit $2007 read state machine (a small shift-register pipeline that generates the read's ALE at the correct phase) plus a behavioural octal latch (a code byte, which sidesteps the force-high wall entirely). Address = high six bits from the PPU, low eight from that latch. Because the pipeline gives the correct phase, the latch captures its stale byte only when boing2k7 actually happens — precise, where the time-window shim was blunt, so the stress tests stay clean.
這個天花板是純開關級的,不是絕對的。通過 ALERead 的行為層模擬器,靠的正是模擬我們「只有晶粒」的網表所省略的那些零件 —— 一個顯式的 $2007 讀取狀態機(一小段移位暫存器 pipeline,在正確相位產生讀取的 ALE)加上一個行為層八位閂鎖(一個 code byte,完全繞過拉高牆)。位址 = PPU 的高六位、加上那個閂鎖的低八位。因為 pipeline 給了正確相位,閂鎖只在 boing2k7 真正發生時捕捉它的過期位元組 —— 精確,而時間窗 shim 是粗糙的,所以壓力測試維持乾淨。
And this is not a foreign graft. Our engine is already a hybrid: the two dies run at switch level, but everything off-die — work RAM, cartridge ROM, the controller's CD4021 shift register, the mapper — is behavioural. The 74LS373 is a board chip, exactly like the CD4021 already is. Modelling it behaviourally is not a compromise of the switch-level principle; it is the principle's own boundary, drawn where it was always meant to be: the netlist describes the silicon; the board is a separate, legitimately behavioural layer. The fix belongs to a planned analog-aware fork (its board-component mechanism, M5), and it is ALERead that makes the strongest case for why that mechanism is load-bearing, not decorative.
而這不是外來的移植。我們的引擎本來就是混合體:兩顆晶粒跑開關級,但晶粒以外的一切 —— 工作 RAM、卡帶 ROM、手把的 CD4021 移位暫存器、mapper —— 都是行為層。74LS373 是一顆板級晶片,和 CD4021 完全同級。用行為層模它不是對開關級原則的妥協;它就是那個原則自己的邊界,畫在它本該在的地方:網表描述矽晶片;板子是一個獨立的、正當的行為層。這個修法屬於一個規劃中的類比感知分支(它的板級元件機制,M5),而正是 ALERead,為「那個機制是承重的、不是裝飾的」提供了最強的理由。
⟳ Sequel correction (2026-07-16): this "way out" was itself standing on the false premise. The 74LS373 is not off-die-and-unmodelled — it was a switch-level module (u2) all along, so no behavioural latch was ever needed. The fix that actually landed corrects the phase at the CPU↔PPU seam and lets the real transistors do the rest — the M3/M6 route, not M5. See Breaking the ceiling.
⟳ 續集更正(2026-07-16):這條「出路」本身就站在那個錯誤前提上。74LS373 並不是晶粒外未建模的零件 —— 它一直都是一顆開關級模組(u2),所以根本不需要行為層閂鎖。實際落地的修法,是在 CPU↔PPU 接縫上修正相位、讓真的電晶體做其餘的事 —— 走的是 M3/M6 路線,不是 M5。見打破天花板。
6 · What the last test taught6 · 最後一顆教會的事
A netlist looks like the ultimate ground truth — every transistor traced from die photographs. This one test is the sharpest reminder that it is a snapshot of geometric connectivity, not a complete description of a running console. The console is two dies on a board full of analog technique and TTL glue, and one deliberately cruel test can aim straight at the seam between them. We did not "fail" ALERead so much as locate it: map the exact three-way intersection that puts it out of a binary switch-level engine's reach, prove the reach ends there rather than guessing, and hand the next stage a precise, load-bearing reason to build the one mechanism that crosses it. In a project whose whole ethos is falsifiable results, a boundary this cleanly drawn is worth more than a 141 with a shim nobody can explain.
網表看起來像終極的真相 —— 每顆電晶體都從 die 照片描下來。這一顆測試是最鋒利的提醒:它是幾何連通性的快照,不是一台運轉中主機的完整描述。主機是兩顆晶粒放在一塊滿是類比手法與 TTL 膠合邏輯的板子上,而一顆刻意兇殘的測試,能瞄準它們之間的接縫直射。我們與其說「沒過」ALERead,不如說是定位了它:把讓它超出二值開關級引擎射程的那個確切三方交集畫出來、證明射程到此為止而非用猜的、並交給下一階段一個精確而承重的理由去建那個唯一能跨過它的機制。在一個整個精神就是可證偽結果的專案裡,一條畫得這麼乾淨的邊界,比一個帶著沒人解釋得清的 shim 的 141 更有價值。
Sister articles: the semantic ceiling (where the boundary is) · crossing the analog boundary (the plan to cross it). Investigation record: six consults under tools/knowledgebase/a_{aleread,bol,m6}_*, handoffs under MD/toDoNext4/.姊妹文章:語意天花板(邊界在哪)· 跨越類比邊界(跨越它的計畫)。調查紀錄:六輪諮詢在 tools/knowledgebase/a_{aleread,bol,m6}_*,交接在 MD/toDoNext4/。