エミュレータを使う前に
BIOS もゲームの ISO も、必ず自分のPS1・PS2・64・Dreamcast本体・自分のディスク・カセットから用意すること。
ネットで拾ってくるのは論外。ダウンロードも配布も違法。
エミュレータ本体(DuckStation / PCSX2 / Flycast / ares)自体は合法で公式配布されている。問題になるのは常にデータの出どころのほう。
なお自分の持ち物から吸い出す場合も、日本の著作権法には技術的保護手段の回避に関する規定がある。 適法かどうかは各自で確認を。この記事は法的な保証をするものではない。
この作業は Claude(Anthropic の AI)とやりとりしながら進めた。 コードはほぼ書いてもらっている。実機での計測と検証は自分。
ノッチが飛ぶ
PS1 の電車でGO!用コントローラ、SLPH-00051。二ハンドルのやつだ。
これを零環さんの PS1/PS2-USBコンバーター [LAY-002-01] 経由で PC に挿して、DuckStation で遊んでいた。
動かした。
マスコンの P2 が飛ぶ。
ブレーキもいくつか飛ぶ。PCSX2 でも同じだった。最初はスティックの閾値の問題だと思って調整したが、どう弄っても直らない。これはエミュレータ側の問題じゃないか、というのが出発点だ。
プラグインでは直せなかった
エミュ側の問題なら、入力プラグインを書いて直せばいい。そう思って調べたら、道が塞がっていた。
DuckStation はそもそもプラグイン方式を否定して作られた統合型。PCSX2 も 1.7 以降でプラグイン機構を廃止している。どちらにも入力プラグインの API が存在しない。
エミュを触れないなら、手前のコンバーター側で直すしかない。幸い LAY-002-01 の中身は ProMicro 互換で、ファームを自分で書き換えられる。
電GOコントローラの中身
MarcRiera/ddgo-controller-docs に書いてある。要点は3つ。
普通のデジタルパッドとまったく同じプロトコルで喋っている。 特別な通信は何もしていない。
ハンドルはボタンの組み合わせで段を表す。 力行が3ビット、ブレーキが4ビット。
十字キーの上と下が常時押しっぱなし。 ゲームはこの「普通はありえない組み合わせ」で電GOコントローラだと見分けている。
つまり軸はどこにも出てこない。閾値もデッドゾーンも本来関係ない。
それなのに段が飛ぶのは、コンバーターやエミュのどこかで、ボタンの組み合わせを軸やハットスイッチに変換していて、そこで情報が落ちているからだ。HID のハットスイッチは上と下を同時に表現できない。識別用の上下同時押しが、まずここで消える。
なら、16 ビットをそのまま 16 個の独立したボタンとして PC に流せばいい。解釈する層がなくなれば、段は飛びようがない。
Windows のゲームコントローラー画面(joy.cpl)を見ながら、全部の段を一段ずつ読んだ。
常時点灯の 5 と 7(上下)を除くとこうなる。
マスコン 点灯 ブレーキ 点灯
N 6, 8 解除 9, 10, 12
P1 6, 13 B1 10, 11, 12
P2 6 B2 10, 12
P3 8, 13 B3 9, 10, 11
P4 8 B4 9, 10
P5 13 B5 10, 11
B6 10
B7 9, 11, 12
B8 9, 12
非常 全部消灯
15 状態、全部違う組み合わせ。重複はひとつもない。
飛んでいた P2 も、ここではちゃんと「6 だけ」という固有の値を出している。コントローラもコンバーターも正常。犯人はエミュ側の変換で確定した。
ちなみに非常の位置で値がパラパラ変わるのは、汽車でGO! の自動帯のせいだ。ハンドルの不調ではない。
ファームを書く
やることは単純で、PS1 から来た 16 ビットを、軸もハットも通さずに 16 個の HID ボタンへ順番に流すだけ。
ベースは零環さんが公開しているスケッチ(GPLv3)。ATT ピンと SPI の構成をそのまま引き継いだので、配線は触っていない。
困ったのは割り当て画面だ。上下が常時押されているので、エミュの割り当て画面で「ボタンを押してください」と出た瞬間に、上か下が勝手に拾われる。他のボタンが一切割り当てられない。
そこで、SELECT と START を押しながら挿すと上下だけ出力を止めるモードを入れた。LED が赤く光ったらこのモード。
PS1 用のワンハンドル機 TCPP-20001 も試した。
点滅する。
ハンドルを動かすと一瞬ボタンが光るだけで、安定しない。PsxNewLib で TCPP-20001 が動かないという報告は前からあるが、原因は書かれていない。
観察すると、止めた位置で同じ組み合わせが点いたり消えたりしていた。読み取りが時々失敗して、そのたびに全ボタンが離されている。
なので、読み取りに失敗した回は前の値を保持するようにした。100 回連続で失敗したときだけ、本当に外れたと見なす。
止まった。
ビット配置は SLPH-00051 と完全に同じだった。ワンハンドルなのに二ハンドルと同じ表で読める。ファームも設定も共用できる。
書き込めない
本筋ではないが、一番時間を食ったのはここだ。
Switch 向けに作った Zuiki マスコン用のボード定義を選んだまま書き込もうとしていた。あれは CDC を切ってあるので Serial が存在せず、リンクで落ちる。
しかも CDC を切ったファームが載っている間は、COM ポートが現れない。IDE が自動リセットをかける相手がいない。リセットボタンを押せばブートローダーが 8 秒だけ起動するが、手動では間に合わなかった。
結局、PowerShell でポートの出現を 50ms ごとに見張って、出た瞬間に avrdude を叩いた。
while ($true) {
if ([System.IO.Ports.SerialPort]::getportnames() -contains "COM9") {
& "$a\bin\avrdude.exe" -C"$a\etc\avrdude.conf" -patmega32u4 -cavr109 `
-PCOM9 -b57600 -D -Uflash:w:"$hex":i
break
}
Start-Sleep -Milliseconds 50
}
もうひとつ引っかかったのが COM 番号。Pro Micro は、スケッチが動いているときとブートローダーのときで COM 番号が違う。
IDE で選ぶのはスケッチが動いているほう。そっちを選んでおけば、IDE が勝手にリセットをかけて書き込んでくれる。ブートローダー側の番号を選んでいたから「開けない」と言われていた。
CDC ありのボード定義(SparkFun Pro Micro / 3.3V 8MHz)で焼き直してからは、普通の Arduino と同じ感覚で書き込めるようになった。
DuckStation
パッドをデジタルコントローラーにして、16 個を 1:1 で割り当てる。
段は複数ボタンが同時に点くので、GUI で割り当てるのは難しい。設定ファイルに直接書いた。
[Pad1] Type = DigitalController Select = SDL-0/Button0 L3 = SDL-0/Button1 R3 = SDL-0/Button2 Start = SDL-0/Button3 Up = SDL-0/Button4 Right = SDL-0/Button5 Down = SDL-0/Button6 Left = SDL-0/Button7 L2 = SDL-0/Button8 R2 = SDL-0/Button9 L1 = SDL-0/Button10 R1 = SDL-0/Button11 Triangle = SDL-0/Button12 Circle = SDL-0/Button13 Cross = SDL-0/Button14 Square = SDL-0/Button15
ハマったのは設定ファイルの場所。新しめの DuckStation は %LOCALAPPDATA%\DuckStation に保存している。ドキュメント側にも古い settings.ini が残っていて、そっちを何度書き換えても画面に反映されなかった。
P2 が飛ばなくなった。最初の見立てはこれで裏付けが取れた。
PCSX2
電車でGO!3 は PS1 のコントローラが使える。同じやり方で行けると思ったら、ボタンは効くのにハンドルだけ効かない。
PCSX2 のパッドは DualShock2 しか選べない。本物の SLPH-00051 は DualShock2 向けの設定コマンドに応答しないので、ゲームはそこで「これは電GOコントローラだ」と判断しているようだ。エミュの DualShock2 は応答してしまうので、普通のパッド扱いになる。
そこで方針を変えて、USB の「Train Controller(Type 2)」として見せることにした。
ファームに X 軸 = 力行、Y 軸 = ブレーキの出力を足す。値は、PCSX2 が Type 2 として段に区切る範囲のちょうど真ん中に置いた。16 ボタンの出力はそのまま残してあるので、DuckStation の設定には響かない。
コントローラー端子 1 未接続 USB ポート 1 Train Controller / Type 2 Axes Passthrough オフ
ついでに2つ足した。
START だけ押しながら挿すと、ボタンを出さず軸だけ出す。 軸を割り当てるときに、段ごとボタンが拾われないようにするためだ。LED が青になる。
入力プロファイルを分ける。 電GO3 にだけ当てておけば、他のゲームには影響しない。
ひとつ引っかかったのは書式。PCSX2 は生のボタンを SDL-0/JoyButton13 と書く。DuckStation と同じ Button13 で書くと、閉じたときに勝手に別の名前へ書き換えられてしまう。
Flycast
次は DC 版の電車でGO!2。
Flycast には「電車でGO!」コントローラがちゃんと実装されている。ソースを見ると、ボタンのビットをそのままゲームに渡す作りだった。
PS1 と DC の電GOコントローラは、同じビットの組み合わせを、機種ごとのボタン名に載せているだけ。なので並べ替えれば済む。軸の変換はいらない。
役割 ファームの番号 DC のボタン SELECT 0 D START 3 Start A / B / C 15 / 14 / 13 A / B / C 力行 1 / 2 / 3 12 / 7 / 5 Z / Y / X ブレーキ 1 / 2 / 3 / 4 10 / 8 / 11 / 9 上 / 下 / 左 / 右
常時押しの上下(4 と 6)は割り当てない。DC ではブレーキが十字キーに載っているので、割り当てるとブレーキが壊れる。
[digital] bind0 = 0:btn_d bind1 = 3:btn_start bind2 = 15:btn_a bind3 = 14:btn_b bind4 = 13:btn_c bind5 = 12:btn_z bind6 = 7:btn_y bind7 = 5:btn_x bind8 = 10:btn_dpad1_up bind9 = 8:btn_dpad1_down bind10 = 11:btn_dpad1_left bind11 = 9:btn_dpad1_right
[analog] は空にしておく。PCSX2 用に足した軸が、アナログスティックとして拾われてしまうからだ。
困ったのはメニュー。ブレーキを解除に入れていると、十字キーの下・左・右が押しっぱなし扱いになって、Flycast のメニューでカーソルが流れ続ける。
メニューを触るときはブレーキを非常に入れておく。非常は 4 ビット全部オフなので、十字キーが何も押されていない状態になる。
ares
最後に N64 の電車でGO!64。
ares に電GO専用のコントローラはない。ただ N64 版もボタンの組み合わせ方式なので、普通のゲームパッドとして並べ替えればいい。
気になったのは、ares が十字キーの上下・左右の同時押しを打ち消す作りになっていること。調べると C ボタンは打ち消していない。N64 版はブレーキが C ボタン、力行が十字キーの右・上と Z なので、反対方向を同時に押す場面がない。問題なかった。
役割 ファームの番号 N64 のボタン SELECT 0 R START 3 Start A / B / C 15 / 14 / 13 B / A / L 力行 1 / 2 / 3 12 / 7 / 5 十字右 / 十字上 / Z ブレーキ 1 / 2 / 3 / 4 10 / 8 / 11 / 9 C右 / C左 / C下 / C上
電GO64 はコントローラをコネクタ 3 に挿す前提なので、Controller Port 3 に割り当てる。
そして起動直後のコントローラ選択画面で詰んだ。
「コネクタ 1 のコントローラで選択」と書いてある。ポート 1 に何も割り当てていないと、ここから動けない。ポート 1 の左右と A をキーボードに割り当てておくこと。
あと、N64 には L + R + Start の同時押しでリセット扱いになる仕様がある。この並べ方だと C + SELECT + START がそれに当たる。同時に押さないように。
完成
DuckStation PS1 版各種 16 ボタンを 1:1 PCSX2 電車でGO!3 軸にして USB の Type 2 へ Flycast DC 版 電車でGO!2 DC の電GO配置に並べ替え ares 電車でGO!64 ポート 3 に N64 配置で並べ替え
ファームは 1 本のまま。変えたのはエミュ側の割り当てだけだ。
このスクショ撮るの大変でした汗
最初は「DuckStation で段が飛ばなければいいや」くらいの気持ちだったのに、気付いたら 4 つのエミュのソースを読んでいた。
一番おもしろかったのは、軸を通さないほど素直に動くということ。エミュに解釈させる余地をなくすのが正解で、遠回りに見えた「生のビットを 1:1」が、結局どのエミュでも通用した。
この記事について
一連の作業は Claude(Anthropic の AI)とやりとりしながら進めた。隠すことでもないので書いておく。
ファームのコードと設定ファイルの書き換えは、ほぼ Claude に書いてもらった。PCSX2、Flycast、ares の入力まわりのソースを読んで「ビットをそのまま渡している」「C ボタンは打ち消されない」と判断材料を出してきたのも、「閾値じゃない気がする」という自分の勘に、資料で「そもそも軸が存在しない」と裏付けを付けてきたのも Claude だ。自分だけなら何日かかったか分からない。
一方、実機を触るところは全部こちらの仕事だった。joy.cpl を見ながら全部の段を読んで表を作ったのも、TCPP-20001 で「同じ組み合わせが点滅している」と観察して伝えたのも自分だ。非常位置で値が変わるのを「汽車でGO! の自動帯だ」と見抜いたのもこちらで、Claude はハンドルの不調を疑っていた。
間違いもあった。PCSX2 の機能を実在しない名前で説明してきたり、「パススルー機能で直る」と勧めてきたものが、実は動かないままマージされていたり。書き込みで詰まったときは次々に別の手を出してきたが、ボードの選び間違いや COM 番号の取り違えといった、もっと単純な原因を先に潰すべき場面が何度かあった。
AI に投げれば完成する、という話ではない。実機を持っていて、症状を正確に伝えられて、言われたことをその場で試せる人間がいないと一歩も進まない。そういう分担だった。









0 件のコメント:
コメントを投稿