カピバラ父さんのプログラミング処方箋~割り込みによる並列動作

原則的に、プログラムは上から下に、順番に処理されます。
上の処理が完結している前提で、下の処理を行うという言い方もできます。

ソースコードを理解しているプログラマにとっては、当たり前の前提なのですが、 そうでないユーザー、ゲームプレイヤーにとっては、待ち時間は不安なものです。

昨今は「ANR(Application Not Responding)」という指標があり、 一定時間、うんともすんとも言わないプログラムは品質の欠陥を疑われるようです。

X68000の場合、フロッピーディスクからのロード時間や、エンコードされた ゲーム内データの展開処理など、やむを得ない事情で長時間、処理が止まったように 見える場面が発生しますが、プレイヤー満足度を向上させる工夫を考えてみます。

割り込み処理による並列動作

例えば「ファイルをロードしてデータ展開してPCGに登録する」という、 ゲームプログラミングに頻出する一連の処理を、説明上”タスク”と呼ぶことにします。

次のプログラムで、タスクは正常に処理されるものと仮定します。

	.include doscall.mac
	.text
stat:
	bsr	task		* 処理すべきタスク
loop:
	bsr	vsync		* 垂直帰線期間待ち
	bsr	display		* 表示更新
	bsr	update		* データ更新

	* 終了フラグが立つまで繰り返す
	move	quit_flag(pc),d0
	beq	loop
	DOS	_EXIT

task:
	* タスク処理
	:	
	:
	rts

くどい説明かもしれませんが、このプログラムを実行すると、 タスク処理(task)が完了するまでプログラムはブロックされ、 完了後にloopに書かれたゲーム本体のループ処理が行われます。

タスク処理の中にユーザとの対話的要素がなければ、この間、 コンピュータが沈黙しているように見えてしまいます。

これを防ぐために、以下のように組み直してみます。

	.include	doscall.mac
	.include	iocscall.mac
	.text
start:
	lea	vsync_int(pc),a1
	moveq	#1,d1
	IOCS	_VDISPST		* 垂直同期割り込みの設定
	
	lea	task(pc),a1		* 処理すべきタスクの処理アドレスを、task_ptrに格納する
	lea	task_ptr(pc),a0
	move.l	a1,(a0)
loop:
	* 未処理のタスクがあれば実行する
	move.l	task_ptr(pc),d0
	beq	@f
		movea.l	d0,a1
		jsr	(a1)
		lea	task_ptr(pc),a0
		clr.l	(a0)		* 処理が終わったので、処理アドレスをクリアする
	@@:

	* 終了フラグが立つまで繰り返す
	move	quit_flag(pc),d0
	beq	loop
	
	suba.l	a1,a1
	moveq	#1,d1
	IOCS	_VDISPST		* 垂直同期割り込みを解除
	DOS	_EXIT

task_ptr:	.dc.l	0		* 処理すべきタスクアドレス。0なら処理待ちが存在しない

*** 垂直動機割り込みの処理 ***
vsync_int:
	bsr	display		* 表示更新
	bsr	update		* データ更新
	rte

task:
	* タスク処理
	:	
	:
	rts

まず、IOCSコール(_VDISPST)を使って、垂直同期(画面を描画する走査線が、垂直帰線期間に入る、一定時間間隔で実行する動作) のタイミングで割り込みを発生させ、vsync_int処理に割り込ませるように設定します。

vsync_intの中身は、もともとはloop内に置いていた表示更新とデータ更新で、それが終わったらrte命令で復帰させます。

割り込み設定したものは勝手に動くことを期待して、本編の処理は、タスクの取り扱いに集中します。/p>

まず、taskルーチンは直接サブルーチンコールせずに、一旦、task_ptrというワーク変数に保存しています。
move #task,task_ptr という書き方もできますが、私の場合は上の書き方をします。

loopの中では、task_ptrの内容が0でなければ、タスク処理のアドレスをみなして、 jsr命令でサブルーチンコールを行い、処理が終わったらtask_ptrをクリアします。

loop処理は、最初のプログラムと同じく、quit_flagを監視する無限ループになっています。

もし、quit_flagが立ってプログラムを終了することになったら、垂直同期割り込みを解除してから終了します。

プレイヤーを不安にさせないための演出

割り込みを使うと、時間がかかるタスク処理を粛々と進める傍らで、画面表示を更新できます。 そこで簡単なデモンストレーションを動かせば、ロード待ち時間を退屈させずに待つことができます。

デモの内容は工夫次第ですが、その実行にもデータリソースの読み込みが必要なことがあります。 上のプログラムでは、task_ptrが0になったら新しいタスクを投入できるため、 初期化の最初にデモ用リソースをロードし、その後に重めの初期化タスクを追加します。

それと、お気づきになったかもしれませんが、割り込み型のプログラムでは、vsync待ちのからループ処理が不要で、 CPUを効率的に使っています。

ちなみに、割り込み処理でのデータ更新処理を膨らませていくと、よりインタラクティブな演出が可能で、 もっと言えばゲームの本体の処理まで出来ます。ただし、その分タスク処理に割り当てられるCPU時間が減りますので、 タスク完了までの時間が長くなりますから、必要な場面を考えて使い分けることが大事です。


プログラミング処方箋へ

Copyright©2026 カピバラ父さん