原則的に、プログラムは上から下に、順番に処理されます。
上の処理が完結している前提で、下の処理を行うという言い方もできます。
ソースコードを理解しているプログラマにとっては、当たり前の前提なのですが、 そうでないユーザー、ゲームプレイヤーにとっては、待ち時間は不安なものです。
昨今は「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 カピバラ父さん