NES on FPGA 2008/08/25

PAD


例えるならそう、無駄をなくした論理性を生物が
持つ対称性で型どった兄弟たちのような…。

 パッド、いわゆるコンローラです。 NESのゲーム用にはNESのパッドを使用するべきなのですが、 今回はゲームで遊ぶことが主目的ではないことと、入手のし易さ、 汎用的であることからプレイステーションのパッドを流用しました。
 FPGAとの接続には、PSからPADコネクタを取ってきて流用しています。

_
▼ パッドタイミング

 パッドデータの取得はVBlank時に行うため、1秒間に60回の取得となります。 また1Pと2Pで同じ回路を使用しています。

 PSパッドプロトコルで使用されるクロックは250KHzであり、 その立ち上がり、立下りの両方でコマンドビットを制御する必要があるため、 このモジュールは500KHzでドライブします。

_
▼ フォースフィードバック(振動)

 マリオがブロックを下からどつくと振動します。 これは単にアドレス$3CEDか$5CF2にアクセスがあったら振動するようにしてるだけ。 凶悪なモンスターを踏んづけるときは$5969で。 だもんで、他のゲームだと変なところで振動するかな、 と思いきや意外とシーンとマッチしてました。くにおがやられた時とか、 レッキングクルーではしごを上る時とか…。沙羅曼蛇では常に振動してますが。

_
▼ ソフトリセット

 某■のほとんどのソフトで実装されていたソフトリセットを実装しました。 評価ボードのちっこいリセットボタンを押す煩わしさが解消されました。 パッドのスタート、セレクト、R1、L1を同時に押すことで、 CPUに対してリセット信号を送っています。

_
▼ 低速モード

 パッドのL1を押している間、ゲーム速度を半分にします。 多くのゲームにおいてはPPUからCPUへのVBlankによってゲーム速度が 60fpsに調節されています。 こいつを2回に1回断ち切ってやることで30fpsになります。

_
▼ Ver.1 夢のコラボ (初期実装)

 PSパッドに関する資料を参考にして2日がかりで実装。 ちょうど手元にPSノーマルパッド(SCPH-1080)が転がっていましたのでLinkup。 やあ動いた動いた。
 これだけじゃなんなので、ソフトリセット実装。 そしてボタン配置がNESとは異なるため、わりかし不評。
 マイクはつかないんですよ…。

_
▼ Ver.2 ふるうる

 実はデュアルショック(アナログコントローラ、SCPH-1200)も持っていたのでLinkup。 と、認識しない…。
 どうやらプロトコルの信号タイミングがシビアになっていたらしく、 SEL信号の立下りから最初のクロックまでのウェイト時間を増やすと動いた。 新しい資料には書いてあるのね。
 で、こいつには振動機能がついてるので早速実装。 マリオが楽しくなることうけあい。 ただし振動用電源の7Vに3.3Vしか入れてないからよわよわなんですわ。 実際気付かない人が多い…。
 これだけじゃアレなので、低速モード実装。 テスト程度でしか使用することはないと思いますが。
 しかし依然として操作性、悪し。

_
▼ Ver.3 え〜い、買っちゃえ買っちゃえ! 2004/11/26

 ここまで来たらやるしかねえってんで、買ってしまったデュアルショック2(SCPH-10010)。 あっさり認識。
 振動を強化するためボードから5Vを引っ張ってくる。 ん、ふるえておる。
 で、こいつには感圧センサがついてるので、どう利用したものか。

_
▼ Ver.4 これで…すべてか…。 2008/08/09

 DUALSHOCK2の感圧センサの読み取りを実装。なんかNESとは関係なくなってるなあ。 参考までにコードを置いておきますが、動作を保障するものではありません。
 PAD読み取りのみであればCycloneIIで80LC程度で使用できます。 振動およびアナログスティック、感圧を使用するためには #define enable_DUALSHOCKのコメントアウトをはずしてみてください。 回路規模は250〜300LCになります。

pspad_ctrl.sflp 、及びPSPAD_ctrl使用例

%i "pspad_ctrl.h"

	PSPAD_ctrl pad1, pad2;
	output PAD_SEL1n, PAD_SEL2n, PAD_CLK, PAD_CMD;
	input PAD_ACKn, PAD_DAT;

	par{
		PAD_SEL1n = pad1.seln;
		PAD_SEL2n = pad2.seln;
		PAD_CLK = pad1.clk & pad2.clk;
		any{
			pad1.seln==0b0 : PAD_CMD = pad1.cmd;
			pad2.seln==0b0 : PAD_CMD = pad2.cmd;
		}
//		pad1.ackn = PAD_ACKn;
		pad1.data = PAD_DAT;
		pad2.data = PAD_DAT;

		// PAD入力状態をLEDに表示
		LEDR = pad1.data_cur | pad1.data_ref;

		// スティックデータを表示
		HEX3 = seg73.con(pad1.stick_LH<7:4>).oSEG;
		HEX2 = seg72.con(pad1.stick_LH<3:0>).oSEG;

		// 感圧データを表示
		HEX1 = seg71.con(pad1.press_Rect<7:4>).oSEG;
		HEX0 = seg70.con(pad1.press_Rect<3:0>).oSEG;

		// SWで振動
		if(SW<8>) pad1.vibrate(0x18);
	}

// クロック50MHzから500KHzを生成
	stage clk500k {
		reg_wr count500k<7>;
		if(/|count500k) count500k--;
		else{
			pad1.clock_500k();
			pad2.clock_500k();
			count500k := 0b1100011; // 100-1
		}
	}
// 1/60秒ごとにPADのキー入力状態を取得
	stage vblank {
		reg_wr vcount<20>;
		if(vcount==0xCB735){ // 1/60s
			vcount := 0;
			generate pad_get.do();
		}
		else vcount++;
	}

	stage pad_get {
		state_name p1,p2;
		first_state p1;
		state p1 par{
			pad1.key_get();
			goto p2;
		}
		state p2 if(pad1.seln==0b1){
			pad2.key_get();
			goto p1;
			finish;
		}
	}

 DE1ボードへの接続と動作テストを行うものの、 PADのGNDを論理Lに接続していたためモータが振動せず、 またPADの3.3Vに論理Hを接続していたためSCPH-1200でアナログモードにしたときにリセットがかかってしまう (SCPH-10010では問題が出なかった。消費電力が少ないため?)などというアホなミスを犯してしまいました。 ちゃんと接続しましょう。

_
▼ 参考文献

 PSPADコントローラを作成するにあたって次の文献を参考にさせていただきました。 ありがとうございました。

プレイステーション・PAD/メモリ・インターフェースの解析 藤田氏
デュアルショック(SCPH-1200)の解析 寺川氏
DUALSHOCK2の信号解析まとめ 永田氏
デュアルショック2(SCPH-10010)の新機能使用方法 NT氏


Copyright(C) pgate1 All Rights Reserved.