Lab の指摘へのエンジン側の回答
レビュアーの道具は halfwave-lab/findings.md をまるごと書き出すので、そこに
追記した回答はエクスポートのたびに消える (一度そうなった)。このファイルが
会話のエンジン側で、これを上書きするものは何も無い。§1.2 のような節への参照は、
その時点での findings ファイルを指している。
1. 第一回 (2026-08-20)
チェックインの後に追記した。記録が、開かれた場所で閉じるようにするためだ。
1.1 §1.2 への回答: 修正はソルバに入り、不変条件はすでに存在する
これは話 (a) であり、推論によるのではない。修正は
halfphi/src/engine.rs::recalc_node の一行で、グループの解決レベルを各メンバの
記憶へ書き込むループで、いまはレールを飛ばす。直列化の時点で何かをクランプして
いるものは無く、halfshot の書き手はいつも読んでいたのと同じ記憶を読む。修正前は、
両方のレールにつながったグループが Vss へ解決し、vcc 自身の記憶セルに false を
書いていた (リファレンス実装もまったく同じことをする)。そしてそれはソルバには
観測できなかった。レールは同一性で解決し、格納されたレベルでは決して解決しない
からだ。報告されたレベルは間違っていた。ソルバが下流へ流した入力はどれも
正しかった。それが、述べたとおりの (a) だ。
求められていた不変条件も、§1.2 が求めるその場所にすでに存在する。
crates/v6502-sim/tests/functional.rs の
rails_hold_their_level_at_every_half_cycle が、走行の全半サイクルで両レールを
主張し、recalc_node のあの行を戻すと赤になる。グループ解決への将来の変更は、
書き出しの場ではなくテストスイートの中で声を上げて失敗する。
裏付けとなる事実が二つ、どちらも当時の CLAUDE.md に記録されている。一つ、修正は まったく別のダイでの挙動を変えた (Z80 が冷たい電源投入から収束するようになった。 あちらには、跳ねるレールがトグルさせていた vcc ゲートのトランジスタが 32 個ある。 6502 には一つも無い)。直列化側のクランプではこれは起こせない。二つ、「ノード 657 だけが違う」は期待どおりの形だ。vss の格納レベルはすでに解決値と一致していた のだから。
1.2 §2.2 出荷済み
api ページのデモ節は、dpc17_SUMS の横で dpc23_SBAC も監視するようになり、
どちらが保持されどちらが発火するかを、h=36/h=38 の筋書きまで書いて述べる
(コミット caf4ff2)。主張は、その節のレシピを再実行して散文が述べる数字をすべて
導き出すテストが釘付けにしており、ページを壊して赤になるのを見て、見分けられる
ことを確かめてある。§2.2 のサイクル表にあるより鋭い言い方 (「加算は命令が
終わった後に起きる」) は、サイトの Lab とチップマップの案内が ADC について語る
のと同じ話で、正しい。
1.3 §4 の最後の残り
/trace と /halfshot は主題では Lab と重なるが、枠組みでは重ならない。trace は
任意オペコードの表と短絡した線のグループ、halfshot はプレート全体の記録済み
ギャラリーだ。19 辺の背骨を、絵と同じブール値から生成した語りとともに描いている
ものは、サイトに何も無い。あれは新しい。そして §3.3 にある
「ADL->PCL はどのフェッチでも導通し、ジャンプとは PCL->ADL 無しの ADL->PCL だ」
という読みは、本当に良い実測の教えだ。
1.4 提案の一覧、出荷済み (同日)
§2.3、§2.4、§2.6、そして §2.7 の until についての望みの両方、加えて §2.5 の rows の側が動いている:
GET /v1/nodes: 解決可能な名前すべてを id 付きで、グループ分けして (グループ 分けは名前についての書き下ろしの読みであり、そう述べてある)。数は 832 で、 文書にあった 846 は教えるところのある間違いだった。ダイの表には生の項目が 846 あり、そのうち 12 は JS のオブジェクト意味論で潰れる重複キー (リファレンス自身 のパースも同じことをする)、残った 834 のうち 2 はビット 5 の歩哨p5/Pout5だ。nginx はこのルートだけをpublic, max-age=86400で配り、他はすべてno-storeだ。until_pc: あるアドレスのオペコードフェッチでのブレークポイント、エンジンでは RUNTO で、境界ではcompleted: false。until: "cycle"も。- すべての Observation に
alu、sb、adl、adh。それぞれ自分の線から読む。 宿無しの和がついに画面に出た: 加算プログラムの h=37 で、aluとsbはどちらも $42 を読み、aは $2E を読む。A 自身の遷移から半サイクルを導くテストが 釘付けにしている。 - ミドルウェア経由の CORS
*。OPTIONS のプリフライトは 200 を返す。 format: "rows": トレースを列指向の整数行として ({cols, watch_names, rows})、 符号化は述べ、監視はビットマスクに詰める。スイートは、オブジェクト形式と列ごとに 一致することを主張する。実測で、監視 2 個の 45 半サイクルで 3.7 倍、監視 22 個の 133 半サイクルで 7.5 倍小さく、比は監視の一覧が伸びるほど大きくなる。
§1.3 も同じ回で出荷した。halfshot のエクスポートは build (commit、
committed、exported) を運ぶようになった。古いファイルが有効なままになるよう
省略可能で、あるときは check-halfshot.mjs が検証し、実ページのエクスポートに
ついては _halfshot-test.html が主張する。一覧から未決なのは §2.5 の fields
パラメータだけ (代わりに rows を出した)。
1.5 二回目の手直しと、Lab 側の採用 (2026-08-20、後で)
rows の圧縮についての主張は、上の実測の帯として述べ直した (以前は「およそ 10 分の
1」と書いていた。レビュアーは 6.4 倍と測り、異議は正しかった)。45/2 の形を測り
直してページの数字を主張するテストが、それを押さえている。49 本の dpc* 行は
/v1/nodes の decode から独立した datapath グループへ移った。何が駆動するか
ではなく、何を操作するかで仕分けた。分割の合計はいまも 832 で、数は釘付けだ。
どちらもコミット 0ac7b66。
チェックイン済みの Lab (halfwave-lab.html) は新しい面を使うようになった。
トレースには format: "rows"、そして alu/sb/adl/adh のフィールド、
ALU の演算の読み出しと、もう駆動されていないバスの読みに対する「古い値」の扱いも
付いた。
2. 輸送の回 (2026-08-20、パッケージ化された findings と issues/)
パッケージ (halfwave-lab/) は、提出済みの issue 八つとともに届いた。処置:
- 01 監視ビットマスクの精度: 出荷済み。
watchは小文字 hex のビットセットに なった (バイト i/8 の中のビット i、LSB 先頭。状態 blob 自身の約束だ)。幅は固定で、 §5.6 の提案どおりwatch_encoding: "hex"を線に載せる。回帰テストは issue にある 64 個の名前を監視し、オブジェクト形式に対して全行の全ビットを比べる前に、マスクが 2^53 を超えることを要求する。Lab のテンプレートは二モードのwbit()(hex または 旧来の整数、同梱デモ用) を得て、再ビルドして再デプロイした。この修正は、あちらの>>による読みが持っていた 32 ビットの天井も取り除く。 - 02 gzip: 出荷済み、両オリジンで。 四つの API プロキシの location それぞれに
一つのスタンザ (
gzip_types application/json、レベル 5、min 860)。監視 64 個の 40 行の rows 応答は、線の上で 2,417 バイト。静的な halfwave のインデックスは、 すでに全体の text/html 既定で賄われていた。 - 03 ラッチのフィールドを昇格: 受け入れ、次の回。 alu/sb/adl/adh と同じ規律で、 名前を持つ記憶を自分の線から読む。
- 04 TraceRows の文書: 出荷済み。 境界を述べ (「トレースのペイロードだけで 測っている。どちらの形式でも同一のマシンは除く」)、大きさの主張は gzip の 留保を付けて運ぶ。gzip をかければ二つの形式はほぼ収束し、rows はパース時間と アロケーションで元を取る。
- 05 NDJSON ストリーミング: 見送り、issue 自身の数字に基づいて。 TTFB は サーバの計算で、転送は 5 ms を切り、まとめて取る消費側には消すべきステップごとの 往復が無い。段階的な描画に対するステートレスな答えはすでにある: 走行を小さな /v1/step のスライスに分け、マシンが返ってくるたびに描く。新しいプロトコルの 要らないストリーミングであり、しかもワーカ間で並列化する。一つのチャンク応答には それができない。10,000 行を一つの応答で、計算しながら描く必要が消費側に本当に あるなら、また見直す。
- 06 レール修正の場所: 上の §1.1 で決着。 ソルバの一行、そして不変条件は
rails_hold_their_level_at_every_half_cycle。これ無しでは赤になる。 - 07 エクスポートのビルドスタンプ: 決着、先に出荷済み (コミット 5c17f44)。 検証器は、あるときにその形を検査する。
- 08 CI でのエクスポートの不変条件: 検査がある場所で賄っている。 一覧にある
不変条件はどれも
tools/check-halfshot.mjs(差分がレールに触れないこと、再生が 綺麗なこと、h/ph/clk0 の構造、open==controls、詰め物、phi2 で終わる偶数の フレーム数) か Rust のスイート (contested_groups、nonconvergent_settles) が 主張している。CI 自体はこのリポジトリが文書化した意図的な穴で、検査は手元と デプロイの中で走る。
3. issue 03: ラッチ、昇格 (2026-08-21)
alua、alub、idb、idl、dor、abl、abh、pclp、pchp が、すでに
そうだった四つの横で Observation の第一級フィールドになった。どのバイトも自分の
線から読み、オブジェクト形式でも rows の列としても出る。昇格を正直に保つテストは
こうだ: どのフィールドも、自分の 8 ビットを監視して組み直したバイトと、走行の
全半サイクルで等しくなければならない。
デモトレースでの見返りは、機構まるごとが三つのフィールドに出ることだ。h=36 では
オペランドが alua/alub に座り ($2E、$14)、sync はもう次のフェッチに向けて
高い。h=37 では和が alu の中と sb の上に実在し、A はまだ $2E を読み、idl は
もう次のオペコード ($85、STA) を持っている。h=38 で A がそれを取る。「加算は命令が
終わった後に起きる」が、いまフィールドごとに見えるようになり、api ページのデモ節が
まさにそこを歩く。
4. 新しいタブ二つと、それを足すときに捕まえたバグ (2026-08-21)
Lab は Latches タブ (名前を持つ記憶の十三のフィールドを独立した図として、箱は 動きで光る。昇格したフィールドから描く) と Half-cycle タブ (生成した語りを 全幅で、全値の前後の表、そして導通している線) を得た。どちらも Datapath の後ろだ。 issue 03 のフィールドが native になったことで、Lab の二本目の 40 監視リクエストと その合流は、捕捉器自身のコメントが予言したとおりになった。いまは呼び出し一回で、 デモは稼働中の API から取り直したので、同梱の経路と実時間の経路がまた同じ形に なった (hex の watch も含めて)。
入ってくる途中の収穫: bit() はまだ監視マスクを整数として読んでいた
(o.w & (1<<n)) ので、hex への切り替え以降、実時間の経路では制御線の読みがすべて
黙って false になっていた。暗い辺、空の語りの拍、完璧に見えるページだ。hex 変更
後のデプロイ検査は接続バッジだけを確かめていて、これはシミュレータ自身の文書が
警告している「死んだオーバーレイ」の誤りそのものだ。他の二箇所と同じ wbit() で
直し、デプロイの検証はいま、光る辺と自明でない語りを主張する。オーバーレイが
死んだときに失敗する検査は、それだ。
同日の改訂: Half-cycle タブは着いた時点で冗長だったので外へ出した (その語りは Datapath の脇の欄のもの、その表は Latches の図のもの、そのピル (小さなラベル) は Signals タブのものだ)。そして「箱の中」は Datapath タブから出て、独立した Bits タブになった。タブはいま、Datapath、Latches、Bits、Signals、State、 Program。
5. PWA の回 (2026-08-21)
Lab はインストールできる PWA だ。マニフェスト (名前は Halfwave、standalone、
src/make-icons.py が生成する半波のグリフのアイコン、セーフゾーンの中に収めた
maskable 版)、述べた戦略を持つサービスワーカ (シェルはネットワーク優先で、
キャッシュへフォールバック。/api には決して触れない。あそこの応答はどれも、
いま POST したマシンを映すものだからだ)、そして vhost はこの nginx に無い
webmanifest の MIME タイプを得た。一度の CDP セッションでオフラインを証明した:
オンラインでインストールし、ブラウザ側でネットワークを切り、再読み込みする。
ワーカがシェルを配り、アプリは同梱のトレースへ縮退して、七つのタブとラッチの
解説はすべて動く。まさに設計どおりだ。
モバイルも収めた。ヘッダは上端に固定し、計数器、位相のピル、スクラバのラベルは
等幅の数字と予約した幅を持つので、数字が伸びても枠はもう跳ねない。トランスポート
(再生の操作) はフッタで、どの幅でも下端に固定し、セーフエリアのインセットも入る。
ページは横に決してスクロールしない (overflow-x: clip。幅の広い図と格子は自分の
箱の中でスクロールする)。
語りは (要望により) Story タブへ移り、新しく生成する節、ラッチを読むが 加わった。半サイクルごとに、ラッチのバイトと制御のブール値から導いた文が並ぶ: 書き戻しを待っている PC のプライム (そしていま起きているかどうか)、ADL/ADH が 先へ動く間にピンを安定させている出力ラッチ、IDL のバイトとそれが実行中の オペコードかどうか、加算器の入力と結果と、それを誰かが取っているかどうか、 DOR が段に載っているか駆動しているか。命令ごとに書き下ろしたものは無い。
途中の収穫が二つ。vhost の CSP が 'self' 無しの
script-src 'unsafe-inline' だと、サービスワーカの登録が黙って止まる (ワーカは
スクリプトのリソースだ)。そしてヘッドレスの一発読み込みはどれも、非同期の
インストールが終わる前に終了するので、ワーカの検証には --dump-dom ではなく
生かしておくブラウザ (CDP) が要る。--dump-dom の永続プロファイルモードは固まる。
6502 の文書がすでに警告しているとおりだ。
6. 図の一括: Timeline、Decode、Memory (2026-08-21)
新しいタブ三つ。どれも API がすでに配っているデータから描く:
- Timeline: 命令を、交互のレーンの上で時間に対する棒として。セルは T 状態で 着色し、重なりは重なりとして描く。次の棒の先頭にある T+ の半サイクルは、前の 命令のレーンへ塗り戻される。あれはその命令がまだ終わっているところだからだ。 サイトの主題が、ついに横向きになった。
- Decode: PLA の 132 項すべてをビーズとして、名前が持つ T 状態でグループ分けし、 半サイクルごとに光らせる。名前は /v1/nodes の decode グループから来て、トレースの 監視一覧に追加される (hex のビットセットは合計 157 個の名前を涼しい顔で受ける。 issue 01 の修正が買ったものだ)。STA の T2 では、発火しているピルが op-store、 op-sta/cmp、op-T2-mem-zp と読める。デコードされている命令を、シリコンが 名指ししている。
- Memory: 足跡 (時間に対して触れたアドレス、読みは冷たく、書きは暖かく、 クリックで移動) と、ページ別のアクセスの格子。
デモの捕捉は decode の項も運ぶので、三つともオフラインで動く。自走する計測付き ビルドで検証した: ADC の書き戻しで 132 個のビーズのうち 6 個が発火、キャンバスは どちらも塗られており (画素を数えた)、足跡には 2 ページと 21 アドレス、クリックでの 移動はクリックした場所に着く。
7. ピンの制御、加算器、スタック、そしてスタックが見つけたもの (2026-08-21)
StepRequest.pins が res irq nmi rdy so を駆動する (レベルであって、アサートでは
ない)。この駆動はパッドのノードへのプルなので状態のビットセットに住み、ピンは
リクエストをまたいで置かれた場所にとどまる。スイートは、次のリクエストに pins
フィールドが無くても rdy が低で止まったままのマシンと、$FFFE を通してハンドラ
に届き、プッシュされた P で B がクリアである割り込み列とを釘付けにしている。
あれは API が語れなかった唯一の 6502 の話だった。
その上に Lab のタブが二つ。Adder (ALUA/ALUB/桁上げ/SUM をビットの行として。
桁上げの行は計算ではなく、ダイ自身の alucin/C01..C78/alucout から読む) と
Stack (ページ $01 を、S に印を付けてプッシュを強調して。それを埋める
サブルーチンのプリセットも付く)。
スタックのタブはさっそく何かを見つけた。そのポインタが JSR の途中でページの中ほど へ迷い込み続け、タブのバグのように見えたが、そうではない。**JSR のプッシュの サイクルの間、S は呼び先の下位バイトを持っている。**アドレスのラッチが戻り アドレスのプッシュで忙しい間、チップが新しい PCL をそこに停めるからだ。$0211 と $0219 の二つのサブルーチンで確認し (S はそれぞれ $11 と $19 を読み、呼び先を 追っている)、アセンブラ自身のラベル表からサービスのスイートで釘付けにした。タブは いま、測って起きているところでそれを説明する。この注記はプリセットの走行のうち 停めている 25 半サイクルでちょうど発火し、他では発火しない。S がポインタでない間、 ポインタの札は出さない。
8. Interrupt タブ (2026-08-21)
ピン制御の見返りであり、それまで Lab の何も語れなかった 6502 の話だ。ボタン三つが IRQ、NMI、RES を 8 サイクル低にパルスし、チップがしたことを追記する。列はその後、 記述されるのではなく、記録から読み出される。割り込みのプリセットで実測した。 これが機構まるごとを、トレース 6 行で表したものだ:
h=310 read $0206 $4C · IR $E6 · オペコードのフェッチ
h=312 read $0206 $4C · IR $00 <- BRK、BRK は一つもフェッチされていない
h=315 write $01FF $02 · PCH をプッシュ
h=317 write $01FE $06 · PCL をプッシュ
h=319 write $01FD $20 · P をプッシュ <- B はクリア: BRK ではない
h=321 read $FFFE $00 · ベクタ下位
h=323 read $FFFF $03 · ベクタ上位
h=324 read $0300 $EA · オペコードのフェッチ <- ハンドラ
チップは $4C をフェッチしたのに、命令レジスタは $00 を読む。プリデコードが
それを強制したのであり、それが割り込みの機構そのものだ。ベクタの引きは、他の何の
ためにも存在しないタイミング鎖自身の隠れた VEC0/T6 状態が見つける。だからタブは、
サイクルを数えるのではなく測ることで、この列の場所を特定する。プリセットは自分の
メモリ (ハンドラと、そこへ届くベクタ) を運べるようになった。間の 64 KiB を埋め
ずには、どんなアセンブリのソースも置けないものだ。