The whole chain整條鏈Two pins, mixed, filtered, amplified兩支腳,混音、濾波、放大
The 2A03 exposes audio on two pins, and — unlike a normal DAC — they don't output a voltage. They are open-drain NMOS current sinks: they only pull current toward ground. The board turns that into a voltage, mixes the two, and amplifies it. As with video, the RF path is optional — the composite/AV output is plain line-level baseband, so we can tap it directly.
2A03 在兩支腳上輸出音訊,而且 —— 跟一般 DAC 不同 —— 它們不輸出電壓。它們是開漏極 NMOS 電流下拉:只把電流拉向地。主機板把它變成電壓、把兩支混起來、再放大。跟視訊一樣,RF 路徑是選配 —— composite/AV 輸出就是純 line-level baseband,可以直接接。
The famous non-linear mix著名的非線性混音Why louder isn't proportional為什麼越大聲不成正比
There is no R-2R DAC. Each channel's level opens a bank of parallel NMOS-to-ground switches, so a bigger digital value lowers the pin's effective resistance to ground. With the board's 100 Ω pull-up to 5 V that forms a voltage divider — and because conductances (not resistances) add in parallel, the output is a compressive, non-linear curve: the step from 0→1 is loud, the step from 14→15 barely moves. Blargg fit it precisely (output normalised 0…1):
這裡沒有 R-2R DAC。每個聲道的音量開一排並聯的對地 NMOS 開關,所以數位值越大,腳對地的等效電阻越小。配主機板 100Ω 上拉到 5V 就成了分壓 —— 而因為並聯相加的是電導(不是電阻),輸出是一條壓縮式、非線性曲線:0→1 那一步很大聲,14→15 那一步幾乎不動。Blargg 精確擬合(輸出正規化 0…1):
tnd_out = 159.79 / ( 1 / (tri/8227 + noise/12241 + dmc/22638) + 100 ) // Pin 2
The 100 in each denominator is that external 100 Ω pull-up; the other constants are the NMOS array's per-process on-resistances. Two consequences fall straight out: channels are not independent (two pulses at half volume ≠ one at full), and the whole thing is quieter and warmer than a naive linear sum.
每個分母裡的 100 就是那顆外部 100Ω 上拉;其餘常數是 NMOS 陣列的製程導通電阻。兩個結果直接掉出來:聲道不獨立(兩個半音量 pulse ≠ 一個全音量),而且整體比天真的線性相加更小聲、更溫暖。
The board circuit主機板電路A logic gate pretending to be an op-amp一顆假裝成 op-amp 的邏輯閘
On an NES-CPU-11 board, after the two pins:
在 NES-CPU-11 板上,兩支腳之後:
- 100 Ω pull-ups (R7, R8) to +5 V — turn each pin's sink current into a voltage.
- 100Ω 上拉(R7、R8)到 +5V —— 把每支腳的下拉電流變成電壓。
- Mixing resistors: 20 kΩ (R9) on pulse, 12 kΩ (R10) on TND, joined at a mixing node. (The Famicom uses different values — that's why US and JP consoles have a different square-vs-triangle balance.)
- 混音電阻:pulse 串 20kΩ(R9)、TND 串 12kΩ(R10),接在一個混音節點。(Famicom 數值不同 —— 這就是美版/日版方波對三角波音量比不一樣的原因。)
- 1 µF coupling cap (C21) — DC-blocks the mix, a ~90 Hz high-pass.
- 1µF 耦合電容(C21) —— 隔掉混音的直流,~90Hz 高通。
- 74HCU04 (U7) unbuffered inverter as the amplifier — the NES uses no op-amp; it wraps a 100 kΩ feedback resistor (R12) around one logic inverter to force it into its linear region.
- 74HCU04(U7)無緩衝反相器當放大器 —— NES 沒用 op-amp;它在一顆邏輯反相器上跨 100kΩ 回授(R12)硬逼進線性區。
- Output RC filter — roughly a 440 Hz high-pass and a 14 kHz low-pass, out to the RCA jack. Cartridge expansion audio (VRC6, FDS, Sunsoft 5B…) mixes in here on the Famicom via edge pins 45/46; the NES routed it to an unused expansion pin.
- 輸出 RC 濾波 —— 約 440Hz 高通 + 14kHz 低通,到 RCA。卡帶擴充音訊(VRC6、FDS、Sunsoft 5B…)在 Famicom 經金手指 pin 45/46 混進這裡;NES 把它接到一支沒用的擴充腳。
Doing it ourselves自己做A 1.789 MHz sidecar, our own DSP1.789MHz sidecar,自己寫的 DSP
As with video, we'll write the DSP ourselves — not blargg's Nes_Snd_Emu, not a copy of Mesen's filters. The whole analog chain reduces to a handful of first-order IIR filters at the CPU clock:
跟視訊一樣,我們會自己寫 DSP —— 不用 blargg 的 Nes_Snd_Emu、不抄 Mesen 的濾波器。整條類比鏈化簡成 CPU 時脈上的幾個一階 IIR 濾波器:
// per CPU cycle (Fs = 1.789773 MHz) — read the 5 channel values, mix non-linearly s = 95.88/(8128/(p1+p2)+100) + 159.79/(1/(tri/8227.+noise/12241.+dmc/22638.)+100); // analog board = cascaded first-order IIR: y = a0·x + a1·x1 + b1·y1 s = hpf(s, 90); // 1µF coupling (DC block) s = hpf(s, 440); // NES amp/output net (Famicom: a single ~37 Hz) s = lpf(s, 14000); // RC output low-pass // down-sample 1.789 MHz → 48 kHz (ratio 37.286, NON-integer). Never decimate raw — // a square wave has infinite harmonics and would alias horribly. Band-limit first: out48k = polyphase_FIR_decimate(s); // FIR LPF (cut ~20 kHz) then fractional resample
Build order, each step audible: non-linear DAC + naive resample (you already hear SMB, a bit harsh) → add the three IIR filters (it warms up) → replace the resampler with a proper band-limited polyphase decimator (the aliasing hiss goes away) → optional expansion-audio mix.
建置順序,每步都能聽:非線性 DAC + 天真重採樣(已能聽到瑪利歐,有點刺) → 加三個 IIR 濾波器(暖起來) → 把重採樣換成正規帶限 polyphase decimator(混疊嘶聲消失) → 選配擴充音訊混音。
Where it plugs in接在哪The AprVisual integration — a planAprVisual 整合 —— 一個計畫
- Tap the DAC gate nodes. In the switch-level 2A03, find the digital lines from the five channel output latches to their NMOS DAC gates; each CPU cycle, read the five integers (0–15, or 0–127 for DMC). That is the clean cut between "bit-exact digital" and "analog behavioural" — no SPICE for the 100 Ω divider.
- 接出 DAC 閘極節點。在開關級 2A03 裡找五個聲道輸出 latch 到 NMOS DAC 閘極的數位線;每 CPU cycle 讀五個整數(0–15,DMC 0–127)。那就是「bit-exact 數位」與「類比行為」的乾淨切點 —— 100Ω 分壓不跑 SPICE。
- A 1.789773 MHz audio sidecar — the non-linear DAC + the IIR chain + our own polyphase decimator to 48 kHz, exactly as above.
- 1.789773MHz 音訊 sidecar —— 非線性 DAC + IIR 鏈 + 我們自己的 polyphase decimator 到 48kHz,如上。
Honest status. A roadmap, not built — nothing here exists in the switch-level engine yet, and like the video chain it's a presentation layer that changes what you hear, not a bit of the golden-checksum logic. The AprNes reference emulator already has a working APU + filter path (APU.cs, WaveOutPlayer) to compare against; what's new here is generating the analog audio from the transistor-level APU and writing the DSP ourselves. Pairs with the video chain to complete the mixed-signal picture.誠實狀態。路線圖、還沒做 —— 開關級引擎裡一點都還沒有,而且跟視訊鏈一樣是呈現層:改變你聽到什麼,不動金 checksum 的邏輯。AprNes 參考模擬器已有能用的 APU + 濾波路徑(APU.cs、WaveOutPlayer)可比對;這裡新的是從電晶體級 APU 產生類比音訊、並自己寫 DSP。與視訊鏈成對,補完整個混合訊號圖。
← back to deep dives · the other half: the video chain← 回深入專文 · 另一半:視訊鏈