What MetalNES added over Visual6502MetalNES 相對 Visual6502 加了什麼

A supplement: AprVisual S1 is built on MetalNES, which is itself built on Visual6502's chipsim.js. Here's the middle link — the approaches and accelerations MetalNES added on top of the original JS simulator. Line numbers: MetalNES wire_module.cpp; the JS side is chipsim.js (Brian & Barry Silverman, MIT).

補充資料:AprVisual S1 建立在 MetalNES 上,而 MetalNES 又建立在 Visual6502 的 chipsim.js 上。這頁講中間那一段 —— MetalNES 在原始 JS 模擬器之上加的做法與加速。行號:MetalNES wire_module.cpp;JS 端是 chipsim.js(Brian & Barry Silverman,MIT)。

Switch-level model開關級模型
Bryant, 1980s — the theoryBryant,1980s — 理論
Visual6502
JS, single 6502, chip viewerJS,單顆 6502,晶片檢視器
MetalNES
C++, full NES board simC++,整台 NES 模擬
AprVisual S1
C# + Rust, optimizedC# + Rust,最佳化

New to this? Start with the switch-level simulation primer — it explains netlists, conduction detection, the settle queue, and the graph/BFS concepts behind all of this.第一次看?先從開關級模擬入門科普開始 —— 它解釋 netlist、偵測導通、settle 佇列,以及這一切背後的圖學 / BFS 概念。

The Visual6502 base, in one paragraph先一段講 Visual6502 的基底

Visual6502's chipsim.js is a beautifully compact switch-level core (~200 lines): recalcNodegetNodeGroup (recursive addNodeToGroup over conducting channels) → getNodeValue → write states and toggle transistors. It models one 6502 as JavaScript objects (each node has .c1c2s, .gates, .pullup, .pulldown, .state; each transistor carries an .on boolean). It's a research/visualization tool — clarity first, speed second. MetalNES keeps the exact same algorithm shape but re-engineers almost every mechanism underneath for a full NES board in compiled C++.

Visual6502 的 chipsim.js 是一個極精簡的開關級核心(約 200 行):recalcNodegetNodeGroup(遞迴 addNodeToGroup 走導通通道)→ getNodeValue → 寫入狀態並切換電晶體。它把一顆 6502 模成 JavaScript 物件(每個節點有 .c1c2s.gates.pullup.pulldown.state;每顆電晶體帶一個 .on 布林)。它是研究/視覺化工具 —— 清晰優先、速度其次。MetalNES 保留完全相同的演算法骨架,但把底下幾乎每個機制都為「整台 NES + 編譯式 C++」重新工程化。

A. Engine & acceleration changesA. 引擎與加速上的改動

MetalNES changeMetalNES 的改動Visual6502 chipsim.jsMetalNES
Stateless transistors無狀態電晶體 Each transistor stores t.on, explicitly toggled by turnTransistorOn/Off whenever a gate node changes.每顆電晶體存 t.on,gate 節點變動時由 turnTransistorOn/Off 顯式切換。 No per-transistor state at all — conduction is read inline as _node_states[gate] during the walk (L1969). The whole transistor-state array + turn-on/off bookkeeping is gone.完全沒有電晶體狀態 —— 走訪時直接讀 _node_states[gate](L1969)判斷導通。整個電晶體狀態陣列 + 開關記帳都拿掉了。
Bucketed channels通道分桶 One c1c2s list per node; GND/VCC are walked as group members and detected later via arrayContains(group, ngnd/npwr).每節點一個 c1c2s 列表;GND/VCC 當成 group 成員走訪,之後用 arrayContains(group, ngnd/npwr) 偵測。 Channels are pre-split into _tlist_c1c2s / _tlist_c1gnd / _tlist_c1pwr; supply channels just set a flag with early-break (L1975–1999) and are never walked as members.通道預先拆成 _tlist_c1c2s / _tlist_c1gnd / _tlist_c1pwr;supply 通道只設旗標 + early-break(L1975–1999),不當成員走訪。
Flags + 256-LUT旗標 + 256 查表 getNodeValue loops the group checking pullup → true, pulldown → false, state → true, else false. No flag set.getNodeValue 迴圈逐節點查 pullup → truepulldown → falsestate → true,否則 false。沒有旗標集合。 OR-accumulates each node's flags during the walk, then a precomputed 256-entry _flags_to_state LUT resolves it (L2024) — plus extra flag types: forcecompute, set_high, set_low.走訪時 OR 累加每節點旗標,再用預算的 256 項 _flags_to_state 查表解析(L2024)—— 還多了 forcecomputeset_highset_low 旗標。
Capacitance tie-break電容 tie-break A floating group returns the first node it finds with state==true, else false — order-dependent, no capacitance concept.floating group 回傳第一個 state==true 的節點,否則 false —— 順序相關、無電容概念。 Tracks _max_connections / _max_state: a floating group holds the state of its largest-capacitance member (L1944–1947, L2019) — a parasitic-capacitance model.追蹤 _max_connections / _max_state:floating group 保留最大電容成員的狀態(L1944–1947、L2019)—— 寄生電容模型。
Flat SoA layout扁平 SoA 佈局 Nodes and transistors are JS objects with array fields; heavy pointer/property indirection.節點與電晶體是帶陣列欄位的 JS 物件;大量指標/屬性間接。 Flat arrays: _transistor_list (null-terminated int sub-lists), _node_infos struct array, _node_states bytes — cache-friendly, compiled-friendly.扁平陣列:_transistor_list(null 結尾的 int 子列表)、_node_infos struct 陣列、_node_states bytes —— 對 cache 與編譯友善。
Persistent settle queue常駐收斂佇列 recalcNodeList does recalclist = new Array(); recalcHash = new Array(); every settle iteration — constant re-allocation.recalcNodeList 每一輪收斂都 recalclist = new Array(); recalcHash = new Array(); —— 不斷重新配置。 Swaps two persistent buffers (_recalc_list_recalc_list_next) and reuses the hash (L1552–1553) — zero per-iteration allocation.交換兩個常駐 buffer(_recalc_list_recalc_list_next)並重用 hash(L1552–1553)—— 每輪零配置。
Compiled C++編譯式 C++ Interpreted JavaScript in a browser.瀏覽器裡的直譯 JavaScript。 Native C++ — the raw language speedup is large, but it's a substrate change, not an algorithm change.原生 C++ —— 純語言加速很大,但那是底層換掉,不是演算法改動。

B. The big capability extension — a whole NES, not one chipB. 最大的能力延伸 —— 整台 NES,不是一顆晶片

Visual6502's chipsim.js simulates one 6502 die, loaded from a single segdefs/transdefs/nodenames set. MetalNES adds a module / composition system: it wires the Visual 2A03 and Visual 2C02 dies plus the board's TTL glue (74LS373 / 74LS139 / 74LS368 / 74HC04), the controllers, and the cartridge into a single flat netlist (a "connection" becomes a permanently-on transistor). On top of that it adds behavioral handlers the pure switch-level core has no concept of — RAM/ROM arrays (simulated as byte arrays hung off control-pin nodes), the master clock, and composite video / audio output. That turns a single-chip viewer into a full NES-001 board simulator. (AprVisual S1 keeps this module system and the behavioral handlers, and adds the batch-settle optimization to them.)

Visual6502 的 chipsim.js 模擬一顆 6502 晶粒,由單一組 segdefs/transdefs/nodenames 載入。MetalNES 加了模組 / 組裝系統:把 Visual 2A03 與 Visual 2C02 晶粒,加上主機板 TTL glue(74LS373 / 74LS139 / 74LS368 / 74HC04)、手把、卡帶,接成一張扁平 netlist(「connection」變成一顆永遠導通的電晶體)。在這之上再加純開關級核心沒有的行為 handler —— RAM/ROM 陣列(掛在控制腳節點上、用 byte 陣列模擬)、主時鐘、composite 影像 / 音訊輸出。這把單晶片檢視器變成完整的 NES-001 主機板模擬器。(AprVisual S1 保留了這套模組系統與行為 handler,並對它們加了 batch-settle 最佳化。)

What both keep the same兩者保留相同的部分

The core algorithm is unchanged from Visual6502: settle by re-evaluating connected node groups until quiescent, with a ~100-pass loop limiter; resolve a group by priority (GND > VCC > pull). MetalNES didn't reinvent the simulation — it kept Visual6502's elegant idea and made it fast and whole-system. AprVisual S1 then took MetalNES's version and pushed the wire core further still.

核心演算法跟 Visual6502 一樣:反覆重算連通節點 group 到穩定,帶約 100 次的迴圈上限;以優先序解析 group(GND > VCC > pull)。MetalNES 沒有重新發明模擬 —— 它保留了 Visual6502 優雅的點子,把它變快、變成整機。AprVisual S1 再拿 MetalNES 的版本,把 wire 核心又往前推一步。