6502tinymachines

高速 2A03 の計画

N3 の計画。作業の前に書いた。

コンソールのスケッチ (nes-bus、docs/nes-end-to-end-v0_2.md、マイルストーン N3) が求めているのは v2a03-micro だ: コアには 6502 のラング 3 を、10 進補正を 切り離して N1 のダイバージェンスの一覧に照らして検証したもの。加えて APU を、 6502 のデコードがそうされたのと同じやり方でラング 0 から実測した表として。それ を測るプローブは成果物として残す。この文書はその計画で、ラングより前に書いた。 各ステップが何を実測したかは docs/n3-report.md が持つ。順序はスケッチ自身の ものだ: まず測り、その測定から書き下ろし、書き下ろしたものをチップに押さえる。

ステップ 1: ダイバージェンスの一覧 (ピンのロックステップのゲート、その後半)

N1 はチップをまたぐ比較を「両方のチップに手が届くところ」に委ねていた。ここ では 6502 のエンジンなしで両方に手が届く。6502 のリポジトリは自分のラング 0 を ピンのところでテキストとして記録していて (tools/pin-golden/*.pins、274 トレース、NC-SA 由来なのであちらでは gitignore)、v6502-pins (MIT、ダイデータ なし) がそれを解析して再生する。だから後半は、隣のチェックアウトの記録を読む このリポジトリの中のテストになる (PIN_GOLDEN= が別の場所を名指す。記録が無い ときは名指しで SKIP する)。

  • src/pins.rs: 2A03 のコアを v6502_pins::PinEngine として (CorePins)、 clk0 の位相ごとに 1 フレーム、規約自身の run で駆動するので、スクリプトが 二つのチップに違う形で当てられることはない。二つのリセット列の間の位置合わせ は実測し (examples/lockstep-probe.rs)、その後は電源投入のたびに表明する。
  • src/lockstep.rs: 比較。違っているフィールドはどれも、名前のある規則で分類 する。名前の無いものは大声を出して失敗する。
  • ゲート: どのトレースも、名前の付いたクラスを除いてピン単位で一致し、その クラスはそれぞれ厳密に境界が付いている (スタックのオフセットは二つのダイ自身の S レジスタから導く。10 進のストアはプログラムのオペランドからバイトごとに 列挙する。リセットの窓はスクリプトとベクタの読みで囲む。write-phi1 のバイトは 数える)。MUTATE=1 は手当て済みのビットを一つ反転させ、名前の無い差として 赤にならなければならない。

ステップ 2: コアのラング

v2a03-micro は v6502-micro に依存し (6502 リポジトリへの git 依存、 リビジョンでピン留め。その表はビルド時に 6502 のラング 0 から実測され、NC-SA 由来なのでコミットは決してしない)、ステップ 1 の一覧が正当化する二つのつまみで それを設定する: set_decimal_adjust(false) と set_stack_at_h0(Some(s))。s は 打ち込むのではなく、このチップのラング 0 のレジスタのノードから読み取る。コアで 他に書き下ろすものは何も無い。

  • ゲート: 記録された各トレースのプログラムを、このチップのラング 0 (CorePins 経由) とコアのラングの上で同じスクリプトで走らせる。両者は write-phi1 の バイトを除くすべてのフィールドで一致する (ステップ 1 が名付けたクラスだ。 コアのラングはそこで 6502 のピンを再現し、その半分ではピンを何も越えない)。 10 進の連鎖はそのまま一致する: 二進のバイト列は 2A03 のものだ。スループットは ラング 0 の横で実測する。
  • MUTATE=1 は補正をつないだままにする: 10 進のトレースは名指しで赤にならな ければならない。
  • コアのラングがこのダイについて再現しないものは、その境界と、それが待っている 測定とともに名前で列挙する (文書化されていない挙動についてのラダーの規則)。 決して覆い隠さない。

ステップ 3: APU のプローブ

ラング 0 からのヘッドレスなダンプ。どれも一つの example で、その出力は、そこ から表を書き下ろす前に報告に記録する:

  • フレームシーケンサ: $4017 への書き込みから始まる完全な 4 ステップの列と 完全な 5 ステップの列にわたって、CPU の半ステップごとの frm_*、そして位相の パルスの CPU サイクルでの位置。
  • チャンネル一つ: タイマー (sq0_t*)、周期、デューティの段 (sq0_c*)、長さ (sq0_len*)、エンベロープ (sq0_env*)、スイープ (sq0_swp*)、出力。
  • 長さの表: 32 個のインデックスすべてを $4003 に書き、読み込まれたカウンタを ノードから読む。
  • ノイズの LFSR とその周期の表、三角波の線形カウンタと 32 段の列、DMC のレートの 表と標本の経路。
  • OUT0..2 と、$4016 への書き込みおよび $4016/$4017 の読み出しでのコントローラの ストローブ。
  • $4014 の書き込み中の RDY (spr_dma_/rdy、アドレスバス、513/514 サイクルの 問い) と、DMC のフェッチ中の RDY (pcm_dma_/rdy)。

ステップ 4: 表としての APU

build.rs がラング 0 を走らせ、表を OUT_DIR に書く (NC-SA なのでコミットは 決してしない): 長さの表、ノイズと DMC の周期の表、デューティの列、そして 両方のモードでのフレームシーケンサの各段の位置。その周りのデータパス (タイマー、 カウンタ、エンベロープ、スイープ、そして src/mixer.rs にすでに書き下ろした ミキサー) はプローブから書き下ろし、札を付ける。

  • ゲート: どのチャンネルも動かすレジスタプログラム。五つの出力コード (AD1/AD2 のタップ、sq0_out、sq1_out、tri_out、noi_out、pcm_out) が CPU の半ステップごとにラング 0 に対してビット単位で厳密に一致すること。 フレーム IRQ と DMC IRQ のタイミングをラング 0 に対して。スループットを実測。 MUTATE=1 は表の項目を一つ落とし、赤にならなければならない。

ステップ 5: ストール

$4014 と DMC のフェッチを、コアのラングの上で RDY が続く区間として、それぞれの 横取りがどの半サイクルに着地するかを見せるラング 0 のゴールデンに対して再生 する。

実時間

2A03 のコアは 1.789773 MHz (マスタクロックの 12 分の 1) で走り、毎秒 358 万 半サイクルになる。6502 のラング 3 は毎秒 3900 万半サイクルと実測されたので、 コアだけなら実時間の約 11 倍の内側だ。APU の表は半サイクルごとの仕事を足すので、 何が残るかは報告が記録する。ゲートは P3 のときと同じ形で述べる: 実時間は性質で あり、余裕とともに実測するものであって、不足が記録されるまで速さのために何も 作らない。

ビルド時に 2a03/docs/n3-plan.md から取り込まれる。写しはリポジトリの一つだけだ。報告は独自の作業用の言葉をいくつか使う: 報告書で使われる言葉。