第10章 VBlank割り込み


サブルーチン

 ソースが長くなってくると、何度も行う処理は一箇所にまとめて、他から呼び出すようにしたくなります。 Cで言えば関数、VBで言えばFunctionと同じことをやるにはどうしたらよいのでしょうか。それにはjsrとrtsを使用します。  第9章のソースgiko009.asmを見ると、62行目以降の「2番目のスプライト座標初期化」と、125行目の「2番目のスプライトの座標更新」はほぼ同じことをしています。 これを一箇所にまとめると、以下のようになります。このような共通化された処理ブロックをサブルーチンと呼びます。

setSprite2:
	; 2番目のスプライトの座標更新サブルーチン
	lda Sprite1_X
	adc #8 		; 8ドット右にずらす
	sta Sprite2_X
	lda Sprite1_Y
	sta Sprite2_Y
	rts

 そして、今までスプライト2の更新を行っていた2箇所を、以下のコードに差し替えます。

	; 2番目のスプライト座標更新サブルーチンをコール
	jsr setSprite2

 ソースはこれです。jsrでサブルーチンへジャンプして、rtsでjsrの次の行に戻る仕組みということです。

VBlank割り込みハンドラ

 VBlankについては、第5章で既に説明しましたが、1/60秒毎にVBlank同期待ちをしてから処理を行うのは、ファミコンのプログラムでは一般的では無いようです。 実際のファミコンゲームでは、VBlank割り込みを使用することが多いようです。割り込みについては第2章で少し説明しましたが、 ファミコンの6502はVBlank突入のタイミングで、必ずNMI割り込みを発生させることができます。このNMI割り込みハンドラにメインループのアドレスを登録しておけば、 1/60毎に処理を行うことが可能です。

 上のnesasm010.asmをさらに改造しましょう。まず、割り込みベクタテーブルは以下のように直します。


	.bank 1      ; バンク1に切り替える
	.org $FFFA   ; $FFFAから開始

	.dw mainLoop ; VBlank割り込みハンドラ(1/60秒毎にmainLoopがコールされる)
	.dw Start    ; リセット割り込み。起動時とリセットでStartに飛ぶ
	.dw 0        ; ハードウェア割り込みとソフトウェア割り込みによって発生

 Startは、最初にNMI割り込み禁止にします。初期化中にmainLoopが実行されたらまずいからです。VBlank待ちも従来通り行います。 そして今までどおり初期化した後、以下のようにNMI割り込みを許可します。第3章で説明したとおり、$2000の7ビット目を1にします。

	; PPUコントロールレジスタ1の割り込み許可フラグを立てる
	lda #%11001000
	sta $2000

 $2000は書き込み専用レジスタなので、$2000をロードしてoraすることはできません。私は間違えてハマってしまいました。

 そして、後は無限ループで待ちます。1/60秒毎に割り込みが発生して、mainLoopが呼び出されます。


infinityLoop:	; VBlank割り込み発生を待つだけの無限ループ
	jmp infinityLoop

 最後に、mainLoopの終わりに割り込み復帰命令を記述します。mainLoop処理が終わると、上のinfinityLoopに戻るようになります。

NOTHINGdown:
	; 2番目のスプライト座標更新サブルーチンをコール
	jsr setSprite2

	rti	; 割り込みから復帰

 VBlank割り込みに対応したソースがこれです。実行しても特に見た目は変わりませんが・・・。

シーケンス図

 ところで、直しているうちに現在作っているサンプルの処理の流れが分からなくなってしまった人が多いのでは無いでしょうか。コメント付きソースを配布してるとはいえ、 我ながらかなり不親切になってきたかなと思います。そこで、UMLシーケンス図でgiko010b.asmのフローを表現してみました。 まー、昔からあるフローチャート図でもいいんですけどね。


トップページ 前のページ 次のページ
SEO [PR] 爆速!無料ブログ 無料ホームページ開設 無料ライブ放送