Chapter.11 メインループの処理と乱数発生ルーチン実験
[Chapter11 制作物]

今回は、そろそろ遅さが目立ってきたメインループをシェイプアップして、少し速度を回復することと
ゲームには欠かせない乱数発生ルーチンのアルゴリズムについて考察するところまでやりたいと思います。

■メインループの処理
chapter.10 の test.bas を見てください。

プログラム1 chapter.10のtest.bas
100 CLEAR 200,&HC000:DEFINTA-Z:COLOR15,4,0:SCREEN4,2,0
110 BLOAD "CHAPT10.BIN"
120 DEFUSR=&HC000
130 A=USR(0)+USR(2)+USR(3)+USR(4)+USR(9)+USR(11)+USR(12)+USR(15)
140 '
150 A=USR(1)+USR(5)+USR(6)+USR(7)+USR(13)+USR(14)+USR(16)
160 IF S=0 THEN IF STRIG(0) OR STRIG(1) THEN S=1:A=USR(8) ELSE ELSE S=STRIG(0) OR STRIG(1)
170 A=USR(10)+USR(17)
180 GOTO 150
		


130行が初期化の処理、150〜180行がメインループになっています。
初期化処理に関しては、全て機械語の呼び出しになっています。
USR(n) は、現状返値を返していません(n がそのまま帰ってくる)が、BASICからは関数扱い
なので、USR(0) のような命令として記述することが出来ません。
あくまで関数として A=USR(0) という形を採ってます。
そのため、130行で求められる A の値は意味を持ちません。必ず 0+2+3+4+9+11+12+15 (つまり 56) が
代入されます。
BASIC は、そんなことお構いなしに、与えられた数式を素直に計算するので、"0+2+3+4+9+11+12+15 を計算する"
という無駄な時間を消費しています。

一方で、test_main.asm を見てください。

プログラム2 chapter.10のtest_main.asm
test_main::
		; USR(n) の n の下位8bitを得る
		inc	hl
		inc	hl
		ld	a, (hl)

		; 0 なら player_init のテスト
		ld	ix, #player_info
		or	a
		jp	z, player_init
		; 1 なら player_move のテスト
		dec	a
		jp	z, player_move
		; 2 なら shot_init(0) のテスト
		ld	ix, #shot_info0
		dec	a
		jp	z, shot_init
		以下略
		


USR(n) 関数を呼び出すと、C000番地にマッピングされる test_main:: が call されます。
hlレジスタには、USR(n) の n が格納されているメモリのアドレスが代入されており、
n が整数型なら、最初の 2byte は無意味で、その後の 2byte に n の値が入ってます。
最初の3命令は、その n を aレジスタに読み出す処理です。

あとは、or a で z フラグを見ることで USR(0) だったのか判断し、player_init へジャンプ
dec a で zフラグを見ることで USR(1) だったのか判断し、player_move へジャンプ
以下略・・・と続くわけです。

つまり、n が何であるか判断する演算が含まれているわけです。

このように、細かい単位で分割しておいて、BASICから自由な順番で呼び出せるようにしておくことで、
それらの細かいルーチンの単体動作テストをやりやすくしていたわけです。
しかし、すでに chapter.11 にもなっており、初期に作ったプログラムは十分動作確認出来ました。
そこで、細かく分断されている「呼び出し」を一つにまとめてしまいます。

BASICで順番に呼び出しているサブルーチンを、アセンブラの中で順番に call するようにして、
まとめていきます。当然ですが、USR(n) の n が何かを判断するルーチンは必要なくなります。
そのようにして、作り替えた test_main.asm の一部を プログラム3 に示します。

プログラム3 新しく作り替えた test_main.asm
test_main::
		call	game_init	; 初期化
		call	game_loop	; メインループ
		ret

; -----------------------------------------------------------------------------
;	初期化処理
; -----------------------------------------------------------------------------
game_init:
		; 自機を初期化
		ld	ix, #player_info
		call	player_init
		; 自機弾を初期化
		ld	ix, #shot_info0
		call	shot_init
		ld	ix, #shot_info1
		call	shot_init
		ld	ix, #shot_info2
		call	shot_init
		; スプライトを初期化
		call	sprite_init
		call	sprite_pattern_init
		ld	hl, #enemy_info0
		call	enemy_init
		; 背景を初期化
		call	background_init
		ret

; -----------------------------------------------------------------------------
;	メインループ
; -----------------------------------------------------------------------------
game_loop:
		; 自機の移動処理
		ld	ix, #player_info
		call	player_move
		; 自機弾の発射処理		★暫定
		ld	ix, #player_info
		ld	iy, #shot_info0
		call	shot_fire
		; 自機弾の移動処理
		ld	ix, #shot_info0
		call	shot_move
		ld	ix, #shot_info1
		call	shot_move
		ld	ix, #shot_info2
		call	shot_move
		; 敵の移動処理
		ld	ix, #enemy_info0
		call	enemy_move
		; 敵の出現処理
		ld	ix, #player_info
		ld	iy, #enemy_info0
		call	enemy_start
		; 敵弾の移動処理
		ld	ix, #eshot_info
		call	enemy_shot_move
		; 背景のスクロール処理
		call	background_scroll
		; スプライトの表示更新処理
		ld	ix, #player_info
		call	sprite_update
		jp	game_loop
		


だいぶシンプルになりましたね。
自機弾の発射処理のトリガー判定は、まだアセンブラに置き換えていませんから、
とりあえず常に発射し続ける(オート連射)にしておきます。

当然ですが、BASIC側で負担していた「順次呼び出し」がアセンブラ側に移動したので、
BASIC側の該当処理を削除する必要があります。
また、0xC000番地をコールすれば動作開始して、機械語の中で勝手にループするようになったので、
DEFUSR宣言も必要なく、BLOAD に ,r オプションを付けて、読み込み後、即実行するようにします。
そのような感じで修正したプログラムを、プログラム4 に示します。

プログラム4 新しいtest.bas
100 CLEAR 200,&HC000:DEFINTA-Z:COLOR15,4,0:SCREEN4,2,0
110 BLOAD "CHAPT11.BIN",R
		


もはや、BASICと呼べるほどのプログラムは残ってないですね (^_^;

実際に動かしてみてください。
メインループ内の BASIC処理部分と、test_main.asm 内の n 判定演算が無くなっただけですが、
明らかに動作速度が速くなっているのを確認出来ると思います。



[▲トップページへ]