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 判定演算が無くなっただけですが、
明らかに動作速度が速くなっているのを確認出来ると思います。
[▲トップページへ]