6502tinymachines

高速 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_HIALE が高い間にラッチされた 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_Xload_vramaddr_v_hscroll_nextノード
INC_Yload_vramaddr_v_vscroll_nextノード
COPY_Xcopy_vramaddr_hscrollノード
COPY_Ycopy_vramaddr_vscrollノード
SET_VBLset_vbl_flagノード
CLR_FLAGSvbl_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 遅延と同じやり方で、図面から打ち込む のではない。

ゲート

  1. ドットゴールデン。 Fast::frame() が可視の 256 x 240 ドットで goldens/p1-dots.bin と全ドット一致すること。ゴールデンの hblank のドット 257..340 はパイプラインの続きの出力を載せており、ステップ 1 の範囲の外だ。 隠すのではなく、ここでそう述べておく。
  2. フレーム時間。 述べた数のフレームをリリースビルドで計測し、平均と最悪の フレームがフレーム周期以内であること。余裕は報告に記録する。
  3. 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 から取り込まれる。リポジトリが唯一の写しだ。報告書は独自の作業用語をいくつか使っている: 報告書で使われる言葉。