高速 2C02 を計画する
P3 計画、作業の前に書かれたもの。
コンソールのスケッチ (nes-bus、docs/nes-end-to-end-v0_2.md) は P3 のゲートを
フレーム時間で述べ、順序も述べている: まずドットごとのステッパを測り、ビット
スライスのデータパスはいま設計するが、不足が記録されて初めて作る。この文書が
その設計で、ステッパより前に書かれている。測られたものは
docs/p3-report.md が載せる。
P3 とは何か
ラング 0 に押さえた書き下ろしの高速 PPU、v6502-micro と同じやり方だ。
シーケンサの表はスイッチレベルのチップから測り出し、データパスは証明済みの
モデルから書き下ろしてそう札を付け、全体をラング 0 のドットゴールデンに押さえる。
実時間は P3 の性質である。ゲートはこうだ: 1 フレームを 1 フレーム周期以内に
描く。12 x f_sc、f_sc = 315/88 MHz (ntsc-grid の副搬送波比) で 714,736 マスタ
半ステップ、すなわち 16.639 ms。述べた数のフレームにわたって測り、平均と最悪、
そして余裕を記録する。
ステップ 1: ドットごとの表駆動ステッパ
表、実測
フレームのドットごとに u16 一つ (262 ライン x 341 ドット)。描画を有効にした
チップがその位置で行う事象で、標準ワールドのラング 0 から
crates/v2c02-fast/build.rs が読み取り、OUT_DIR に書く (NC-SA 由来なので
コミットは決してしない。v6502-micro の表とまったく同じ扱いだ)。各ビットは、
名前を持つノードか CHR バスから来る:
| ビット | 事象 | どこから測ったか |
|---|---|---|
| FETCH_NT, FETCH_AT, FETCH_PT_LO, FETCH_PT_HI | ALE が高い間にラッチされた VRAM アドレス。領域で分類する: $2000 未満はパターンフェッチでビット 3 がプレーン、$2xxx でビット 6..9 がすべて立っていれば属性フェッチ、それ以外の $2xxx はネームテーブルのフェッチ | PpuPins の ale/ad/a_hi |
| SPR_GARBAGE, SPR_PT_LO, SPR_PT_HI | スプライト窓の中の同じフェッチ。窓は clear_spr_ptr がドット 257 でパルスする所から始まり、8 ドットのスプライト八つ分続く | 上と同じ、加えて clear_spr_ptr |
| INC_X | load_vramaddr_v_hscroll_next | ノード |
| INC_Y | load_vramaddr_v_vscroll_next | ノード |
| COPY_X | copy_vramaddr_hscroll | ノード |
| COPY_Y | copy_vramaddr_vscroll | ノード |
| SET_VBL | set_vbl_flag | ノード |
| CLR_FLAGS | vbl_clear_flags | ノード |
ノードの事象は、そのドットの八つの半ステップのどれかでノードが高ければ、その ドットに記録される。半ステップ一つ分のパルスも取りこぼさないためだ。
表がわざと持たないもの: fine_y_eq_7_and_rendering、
vramaddr_v_hpos_eq_31_and_rendering、
vramaddr_v_vpos_29_to_30_transition_and_rendering。これらは v レジスタの値に
ついての条件で、標準ワールドで固定のドットに現れるのはスクロールがゼロだから
にすぎない。これらはデータパスのインクリメント論理のものであり、書き下ろしで、
その実測位置はあの論理への検査であって入力ではない。
実測した位置 (2026-09-03、examples/p3-fetch-probe.rs)
データパスの一行も書く前に、標準ワールドから記録したもの:
- 可視ラインの背景フェッチ: ネームテーブルがドット 8k+1、属性が 8k+3、パターン 下位が 8k+5、パターン上位が 8k+7 で、読み出しは次のドットに着地する。 プリフェッチのタイル二枚は 321..335。ダミーのネームテーブルフェッチが二つ、 337 と 339。粗 X (coarse X) はタイル 33 で次のネームテーブルへ回り込む (ドット 249 のフェッチが $2401 を読む)。
- スプライト窓 257..320: スプライトごとにゴミのネームテーブルフェッチ二つ、 それからパターンの下位と上位。
- INC_X はドット 8、16、...、256、328、336 (1 ラインに 34 回)。INC_Y は 256。 COPY_X は描画される全ラインの 257。COPY_Y はプリレンダーのドット 280..304 に わたる。
set_vbl_flagは (241, 1)。vbl_clear_flagsはプリレンダーラインのドット 1 から 高いまま。in_vblankは (240, 1) からプリレンダーラインを通して。- スプライト評価: スプライトが八つ見つかるラインでは走査がドット 131 で終わる
(
end_of_oam_or_sec_oam_overflow)。そうでなければ OAM の終わりの 193 で終わる。 標準ワールドの OAM は全スロットに Y = 0 を持つので、ライン 0..7 は前者の枝を 通る。ステップ 1 はこれに何も依存しない。これはステップ 2 が書き起こされる 最初の実測だ。
データパス、書き下ろし
15 ビットの v と t、細 x (fine x)。ネームテーブル、属性、そしてパターンの
ラッチ二つ。16 ビットのパターンシフタ二つと属性の選択。32 バイトのパレット RAM。
背景色。インクリメント論理は証明済みのモデルどおり (粗 X は 31 で回り込んで
水平のネームテーブルビットを反転させる。細 Y は 7 で粗 Y へ回り込み、粗 Y は 29
で回り込んで垂直のビットを反転させるか、31 で反転させずに回り込む)。フェッチの
スケジュールと画素が pal_d に現れるまでの遅延は、ドットゴールデンに対して
一度だけ合わせてピン留めする。P1 の DAC 遅延と同じやり方で、図面から打ち込む
のではない。
ゲート
- ドットゴールデン。
Fast::frame()が可視の 256 x 240 ドットでgoldens/p1-dots.binと全ドット一致すること。ゴールデンの hblank のドット 257..340 はパイプラインの続きの出力を載せており、ステップ 1 の範囲の外だ。 隠すのではなく、ここでそう述べておく。 - フレーム時間。 述べた数のフレームをリリースビルドで計測し、平均と最悪の フレームがフレーム周期以内であること。余裕は報告に記録する。
MUTATE=1は表から INC_X を落とす。絵が正しくなりようがなく、ドットの 比較は赤にならなければならない。
ステップ 1 の後、まだ P3 の中
スプライト (評価とフェッチをデータパスに入れ、P2 のスプライトワールドからの ドットゴールデンに押さえる)、それからレジスタファイルとスクロール、それから CPU を付けて blargg のスイートを端から端まで (引き継ぎの第 5 節)、それから 実物の捕捉。
ステップ 2: ビットスライスのデータパス、見積もるが作らない
書き下ろしの見込みで、ステップ 1 の数字に置き換えられるべきもの: ドットごとの
ステッパはドットあたり数十の演算をするので、89,342 ドットは 16.639 ms の周期に
対してミリ秒の前半に収まり、余裕は数倍になる。もし記録されたフレーム時間が周期を
超えるなら、最初の手はタイル (8 ドット) 単位でのバッチ処理だ。フェッチの
スケジュールとシフタはタイル周期だからだ。halfphi::slice のレーン符号化が
並列化するのはマシンであって 1 フレームではないので、あれは多数のコンソールの
ための手であって 1 台のコンソールの実時間のための手ではなく、ここではそう記録して
おく。どちらも実測がそう言うまで作らない。
ビルド時に 2c02/docs/p3-plan.md から取り込まれる。リポジトリが唯一の写しだ。報告書は独自の作業用語をいくつか使っている: 報告書で使われる言葉。