The whole chain整條鏈Where the model stops, and everything after it模型停在哪,以及它之後的一切
Four stages sit between the last digital node we simulate and a picture on a screen. Two live on the 2C02 die, one is a handful of parts on the board, and one is the entire television. The good news up front: the RF modulator does not need modelling — the composite baseband signal is fully formed before it, so we can tap the composite directly.
在我們模擬的最後一個數位節點與螢幕上的畫面之間,有四個階段。兩個在 2C02 晶粒上,一個是主機板上幾顆零件,一個是整台電視。先講好消息:RF 調變器不用模擬 —— composite baseband 訊號在它之前就完全成型,所以我們可以直接接 composite。
Stage 1 — the 2C02 generates階段一 —— 2C02 產生A resistor ladder, not a real DAC一條電阻梯,不是真的 DAC
The NES has no R-2R DAC and no sine oscillator. Its composite waveform is crude switching between fixed DC voltages — a square-wave stack that the television's low-pass filter smooths into colour. On the die: a series resistor divider between VCC and GND, and NMOS pass transistors that connect one tapped node to the output (Pin 21, VOUT) each moment.
NES 沒有 R-2R DAC、也沒有正弦振盪器。它的 composite 波形是粗暴地在固定 DC 電壓間切換 —— 一疊方波,由電視的低通濾波器平滑成色彩。晶粒上:VCC↔GND 間一條串聯電阻分壓,加上 NMOS 傳輸閘,每一刻把某個抽頭節點接到輸出(Pin 21,VOUT)。
- Luma has 4 levels; chroma is a square wave that swings between a "high" and "low" voltage at 3.58 MHz, its phase choosing the hue.
- 亮度有 4 階;色度是在「高」「低」電壓間以 3.58MHz 擺動的方波,相位決定色相。
- Phase encoding: a 12-step ring counter runs at the 21.477272 MHz master clock — which is exactly 6× the 3.579545 MHz colourburst. Each pixel is 4 master clocks; the colour period is 6. They don't divide evenly, so the phase drifts line to line — and that is dot crawl: sample the output at 21.477 MHz and dot crawl appears for free.
- 相位編碼:一個 12 步環形計數器跑在 21.477272MHz 主時脈 —— 剛好是 3.579545MHz colourburst 的 6 倍。每像素 4 主時脈、色彩週期 6,不整除 → 相位逐行漂移 —— 那就是 dot crawl:用 21.477MHz 取樣輸出,dot crawl 自然出現。
- Emphasis (the R/G/B bits of
$2001) turns on an extra pull-down during the off-colour phases, dropping the whole level ~20% — it attenuates the other colours rather than boosting one. - Emphasis(
$2001的 R/G/B 位元)在非該色相位期間導通額外下拉,整體降 ~20% —— 是衰減其他色,不是增強某色。
Then a tiny board amplifier — the 2C02's VOUT is too weak to drive a 75 Ω TV. On an NES-CPU-11 board that's Q1 (PNP 2SA937 / 2SA1015) for level-shift and Q2 (NPN 2SC2021 / 2SC1815) as an emitter follower, plus 220/510/110 Ω and a 220 µF DC-block. Its non-linearity is already baked into the measured voltage table below, so we can skip a SPICE model of it.
接著一個小小的主機板放大器 —— 2C02 的 VOUT 太弱、推不動 75Ω 電視。NES-CPU-11 板上是 Q1(PNP 2SA937 / 2SA1015) 做位準平移、Q2(NPN 2SC2021 / 2SC1815) 當射極隨耦器,加 220/510/110Ω 與 220µF 隔直流。它的非線性已經含在下面的量測電壓表裡,所以可以省掉它的 SPICE 模型。
Stage 2 — the composite signal階段二 —— composite 訊號One scanline, as a waveform一條掃描線,當成波形
$0D −0.11, Luma0 0.22/0.61, Luma3 0.71/1.09.一條 composite 掃描線:低於消隱的 sync tip、colourburst 相位基準,然後 active video —— 底座高度是亮度、3.58MHz 擺動的相位是色相。量測電壓(lidnariq,相對 1Vpp):sync −0.286、blank 0、黑 $0D −0.11、Luma0 0.22/0.61、Luma3 0.71/1.09。Stage 3 — the television decodes階段三 —— 電視解碼Sync → Y/C → demodulate → RGB同步 → 亮色分離 → 解調 → RGB
A real set runs the composite through four stages, historically discrete chips:
真電視把 composite 過四個階段,以前是分立晶片:
| Stage階段 | What it does做什麼 | Classic chip經典晶片 |
|---|---|---|
| Sync separation同步分離 | slice below blanking → H-sync (4.7 µs) & V-sync via RC切低於消隱 → RC 分 H-sync(4.7µs)/ V-sync | LM1881 |
| Y/C | split luma from chroma — notch/bandpass, or a comb filter using a line-delay (glass → CCD → digital)亮度/色度分離 —— notch/BPF,或用行延遲(玻璃→CCD→數位)的 comb filter | delay line / SAA7118延遲線 / SAA7118 |
| Demod + PLL解調 + PLL | lock a PLL to the colourburst each line, multiply chroma down to I/Q每行用 colourburst 鎖 PLL,乘回 chroma 得 I/Q | CXA1621S |
| Matrix矩陣 | YIQ → RGB, drive the electron gunsYIQ → RGB,推電子槍 | TDA3562A · TA7698AP |
The NES comb-filter trap.NES 的 comb-filter 陷阱。 Standard NTSC flips the subcarrier phase 180° per line, so adding two adjacent lines cancels chroma — a comb filter works. But the 2C02, to keep hardware simple, shifts phase only 120° per line (a 3-line cycle). A standard 1-line comb filter does not cancel on NES — colour aliases badly. Famously, expensive comb-filter TVs looked worse on a NES than cheap notch-filter sets. So a faithful model must use a notch filter, which is also what ~90% of 1980s–90s televisions used — and it reproduces the colour bleed and dot crawl correctly.標準 NTSC 每行副載波相位翻 180°,相鄰兩行相加即抵銷色度 —— comb filter 有效。但 2C02 為了硬體簡化,每行只偏 120°(3 行一循環)。標準 1-line comb 在 NES 上不抵銷 —— 色彩嚴重混疊。有名的是:昂貴的 comb-filter 電視看 NES 反而比廉價 notch 電視更糟。所以忠實模型必須用 notch filter —— 那也是 80/90 年代約 90% 電視的做法 —— 且能正確重現色彩溢出與 dot crawl。
Stage 3, our way階段三,我們的做法Our own decoder, no borrowed code自己寫解碼器,不借用程式碼
We will write the decoder ourselves — not nes_ntsc, not a copy of any emulator's filter. The maths is short; the difficulty is timing and filter design. Sampling at 6× the colourburst makes it clean: every colour cycle is exactly 6 samples, so the sin/cos tables are constant and no interpolation is needed. The stages:
我們會自己寫解碼器 —— 不用 nes_ntsc、不抄任何模擬器的濾波器。數學很短,難在時序與濾波器設計。用 colourburst 的 6 倍取樣讓它很乾淨:每個色彩週期正好 6 樣本,sin/cos 表是常數、不用插值。各階段:
// 6 samples per colour cycle (Fs = 6·fsc) sin6[] = {0, .866, .866, 0, -.866, -.866}; cos6[] = {1, .5, -.5, -1, -.5, .5}; // 1. per-line burst phase (software clock is perfect, so no jitter-fighting PLL needed — // just measure the burst; a "cheat" MVP can derive it from (frame+line)%3 instead) burst_phase = atan2( Σ burst·sin6 , Σ burst·cos6 ); // 2. Y/C — NOTCH, never a comb (the 120°/line trap): FIR band-pass around 3.58 MHz C = FIR_bandpass(composite); Y = composite − C; // 3. demodulate against the locked phase, then low-pass off the 2× (7.16 MHz) image I = LPF( C · cos(2πn/6 − (burst_phase+π)) ); Q = LPF( C · sin(...) ); // I~1.5MHz, Q~0.5MHz // 4. FCC YIQ → RGB, clamp 0..1 R = Y + 0.956·I + 0.621·Q; G = Y − 0.272·I − 0.647·Q; B = Y − 1.106·I + 1.703·Q;
Then the CRT, by contribution: scanlines (~40 %, NES is 240p — leave the even field dark), a horizontal Gaussian phosphor blur (~30 %, also softens the dot-crawl noise), gamma ≈ 2.2 (~15 %, or the darks die), and optionally a shadow-mask and bloom. Build order (each step is a working picture): cheat-phase MVP → software burst-lock PLL → tune the FIR orders → CRT filters.
然後 CRT,依貢獻度:scanline(~40%,NES 是 240p —— 偶數場留黑)、水平高斯 phosphor blur(~30%,順便柔化 dot-crawl 噪音)、gamma ≈ 2.2(~15%,否則暗部死黑),以及選配的 shadow mask 與 bloom。建置順序(每步都是能看的畫面):作弊相位 MVP → 軟體 burst-lock PLL → 調 FIR 階數 → CRT 濾鏡。
Where it plugs in接在哪The AprVisual integration — honestly, a planAprVisual 的整合 —— 誠實說,是個計畫
- Tap the DAC control nodes. In the switch-level 2C02, find the digital nodes driving the video-DAC pass transistors:
Luma 0–3, theChroma/ 12-phase select,Sync,Colourburst enable,Emphasis R/G/B. That is the clean cut between "bit-exact digital" and "analog behavioural". - 接出 DAC 控制節點。在開關級 2C02 裡找到驅動視訊 DAC 傳輸閘的數位節點:
Luma 0–3、Chroma/ 12 相位選擇、Sync、Colourburst enable、Emphasis R/G/B。那就是「bit-exact 數位」與「類比行為」之間的乾淨切點。 - A 21.477 MHz sidecar — a lookup
get_composite_voltage(luma, chroma, phase, emphasis, sync)called once per master-clock tick, using lidnariq's measured voltages. No SPICE for the board amp — its non-linearity is in the table. - 21.477MHz sidecar —— 一個查表
get_composite_voltage(luma, chroma, phase, emphasis, sync),每主時脈 tick 呼叫一次,用 lidnariq 的量測電壓。板上放大器不跑 SPICE —— 非線性在表裡。 - Our own decoder on the resulting ~1364-sample-per-line stream, as above.
- 在得到的每行 ~1364 樣本流上跑我們自己的解碼器,如上。
Honest status. This is a roadmap, not a shipped feature — none of it is built in the switch-level engine yet. It is also strictly a presentation layer: it changes how the picture looks, not a single bit of the logic the golden checksum guards. For comparison, the AprNes reference emulator already has a CRT-side (NesCore/NTSC_CRT/) we can check against; what's new here is modelling the analog signal generation from the transistor-level PPU, and decoding it with code we wrote ourselves.誠實狀態。這是路線圖、不是已出貨功能 —— 開關級引擎裡一點都還沒做。它也純粹是呈現層:改變畫面長相,不動金 checksum 守護的任何一個邏輯位元。對照:AprNes 參考模擬器已有 CRT 端(NesCore/NTSC_CRT/)可比對;這裡新的是從電晶體級 PPU 模擬類比訊號產生,並用我們自己寫的碼解碼。
← back to deep dives← 回深入專文