どんばのプログラム


更新日付 1998-07-15

「どんば」のプログラムで特徴的な部分を取り上げ、
変てこりんな解説をする予定です。
もっといいアルゴリズムがあったらツッコミを入れてください。

この講座では、汎用的な技術についてのみ詳しく解説します。
SFC固有の技術についてはソースを解析してください。

[戻る]


・OBJ(スプライト)について

OBJ(スプライト)情報はOAMという領域に格納されます。
このOAMという領域は、メインメモリとは違う世界にあり、
垂直帰線期間しか読み書きできません。
しかし、アクセスの度に垂直帰線期間を待っていては面倒です。
そこでメモリ中に仮想OAMという領域を確保します。
仮想OAMの内容は、垂直帰線期間割り込みを使って、
本来のOAMにDMA転送するようにしました。
X68000では、垂直帰線期間でなくても、
スプライトを描き換えることができましたが、
ちらつきのない美しい表示を実現するため、
当たり前の技術となっていました。
この部分のプログラムについて、詳しい解説はしませんが、
前提として理解しておいてください。

・BGについて

BGはV-RAMに格納されます。
このV-RAMもメインメモリとは違う世界にあり、
やはり垂直帰線期間しか読み書きできません。
背景など、たくさんのBGを書き換えたい場合は、
一度だけ垂直帰線期間を待ち、DMA転送などで一気に転送します。
しかし、この方法はデータをリニアに転送するしかないので、
MAPE形式のデータには使えません。
もし画面が一度消えても構わないのであれば、
画面を表示禁止状態(強制Vブランク)にすることで、
思う存分好きなタイミングで書き換えることができます。

・マップデータの転送例

MAPE形式のマップデータを転送します。
縦に並んでいるので、DMAで転送しても劇的な高速化は望めません。
上で説明したお手軽な転送を紹介します。
------------------------------------------------------------
マップの転送
test_map:
	off16a
	sep	#$20
	lda	#$81		;address +32 at $2119
	sta	#2115		;VMAINC.b

	on16a
	on16i
	rep	#$30
	ldx	#$0000		;転送元アドレス
	ldy	#BG1SC0
_loop:
	sty	$2116		;VMADD.w書き込みアドレス
_put:
	lda	map_data,x	;1キャラロード
	and	#$00ff		;上位バイトクリア
	ora	#$0200		;たとえばパレット1を設定
	sta	$2118		;VMDATA.w
	inx
	txa
	and	#32-1
	bne	_put
	iny
	cpy	#BG1SC0+32
	bne	_loop
	rts
------------------------------------------------------------

このサブルーチンはアドレスが固定なので、あまり使い道がありません。
どんばではもっと汎用性の高い転送ルーチンを使っています。
転送アドレスなどをインラインで記述しているので、
興味があればソースを解析してください。
転送ルーチンの名前はxfer_map:です。

[戻る]


・ポーズの表示

まず現在のアクション番号を調べます。
そして、それをテーブルを使って、
ポーズデータへのオフセットに変換します。
つまりアクションの先頭アドレスを求めたことになります。

次にポーズ番号を調べます。
このアクションにおける何番目のポーズを表示するのかという情報です。
一つのポーズデータは12キャラ×4バイト=48バイトですから、
ポーズ番号は48倍しなければなりません。
かけ算を真面目にするのは、ばかばかしいので、
これもテーブルを使って48倍しています。

上で求めた二つの値を加算すれば、
次のポーズデータのアドレスがわかります。
これにアクション開始時の座標を加算して表示位置を求めます。
こうやって計算した座標データや、OBJ番号などを、
仮想OAMに確保された自機ワーク(pl_xとかpl_No)に転送すれば、
自機キャラが画面に表示されます。
------------------------------------------------------------
;	==============================
;	自機の表示(ポーズデータの転送)
;	==============================
play_disp:
	on16a
	on16i
	rep	#$30

	ldx	p_action		;動作(アクション)
	lda	tableACT,x		;現在の動作(アクション)データのアドレス

	ldx	p_pose			;何番目のポーズか
	clc
	adc	table48,x		;値を48倍するテーブル
	tax				;x=現在のポーズデータ

	lda	base_px
	sec
	sbc	map_x
	sta	temp_px

	ldy	#$0000			;y=転送先アドレス
_loop:
	off16a
	sep	#$20
	lda	base_py			;動作(アクション)開始時のx
	xba
	lda	temp_px			;動作開始時のx、現在A=[py|px]
	on16a
	rep	#$20
	sta	work2			;動作開始時の座標

	lda	pose_data,x		;x,y座標を同時に取り出す
	bne	_skipB
	jsr	bug_trap		;$0000ならバグ
_skipB:
	clc
	adc	work2			;動作開始時の座標
	sta	pl_x+0,y		;OBJ座標x,y書き込み
	lda	pose_data+2,x		;OBJ番号|OBJ属性
	ora	#$3000			;優先度%11、パレット%000、OBJ番号$0xx
	sta	pl_x+2,y		;OBJ属性書き込み
	inx
	inx
	inx
	inx
	iny
	iny
	iny
	iny
	cpy	#48			;1ポーズ=48バイト
	bne	_loop
_exit:
	rts

table48:				;値を48倍するためのテーブル
	dw	00*48,01*48,02*48,03*48
	dw	04*48,05*48,06*48,07*48
	dw	08*48,09*48,10*48,11*48
	dw	12*48,13*48,14*48,15*48
	dw	16*48,17*48,18*48,19*48
	dw	20*48,21*48,22*48,23*48
	dw	24*48,25*48,26*48,27*48
	dw	28*48,29*48,30*48,31*48
------------------------------------------------------------

[戻る]


・アクションの移行

一つのアクションが終わると、次のアクションに移行します。
たとえ何もすることがない場合でも、
それは「立つ」というアクションをしていることになります。

アルゴリズムを説明しましょう。
上と同じ方法で、ポーズデータのアドレスを計算し、
該当するポーズデータの座標を試しに取り出します。
その値が(x,y)=(0,0)、つまり$0000だったら、
次のアクションに移行するという仕掛けになっています。

次のアクション番号は、レバー入力などを考慮して、
やはりテーブルによって求めます。
------------------------------------------------------------
;	==============================
;	自機の移動
;	p_action/p_poseの移行
;	==============================
play_move:
	on16a
	on16i
	rep	#$30

	inc	p_pose
	inc	p_pose

	ldx	p_action		;動作(アクション)
	lda	tableACT,x		;現在の動作(アクション)データのアドレス

	ldx	p_pose			;何番目のポーズか
	clc
	adc	table48,x		;値を48倍
	tax				;x=現在のポーズデータ

	lda	pose_data,x
	bne	_exit

;	動作が終了したので、パーツ01の位置から移動量を計算し、
;	現在の座標(base_px/py)を更新する。
	lda	pose_data+4,x		;パーツ01のx座標
	and	#$00ff
	sec
	sbc	#$0040			;PEDITの編集画面の中央値
	clc
	adc	base_px
	sta	base_px

	lda	p_action		;動作(アクション)
	sta	work2			;×2保存(p_actionは元々2倍なので)
	asl	a			;×4
	asl	a			;×8
	clc
	adc	work2			;8+2=10倍
	sta	work2

	lda	vector			;テンキー形式の入力
	and	#$00ff
	clc
	adc	work2			;×10加算
	tax

	lda	act_table,x		;次の動作(アクション)をロード
	and	#$00ff
	sta	p_action
	stz	p_pose			;先頭のポーズへ

_exit:
	rts
------------------------------------------------------------

[戻る]


・テーブルいろいろ

プログラム中で参照されるデータです。
こういったデータはすべてデザイナがdo_dat.sに記述します。
参考のためここにも掲載しました。

------------------------------------------------------------
;	------------------------------------
;	動作(アクション)移行表
;	動作(アクション)番号とキー入力から、
;	次の動作(アクション)番号を引き当てる
;	------------------------------------
DribL	equ	0			;ドリブルL
DribR	equ	2			;ドリブルR
GDriL	equ	4			;ガード付きドリブルL
GDriR	equ	6			;ガード付きドリブルR
ChgLR	equ	8			;チェンジLR
ChgRL	equ	10			;チェンジRL
LegLR	equ	12			;レッグスルーLR
LegRL	equ	14			;レッグスルーRL
Dash7	equ	16			;ダッシュ7
Dash9	equ	18			;ダッシュ9
BTnLR	equ	20			;バックターンLR
BTnRL	equ	22			;バックターンRL
Fumei	equ	24			;不明

act_table:
;		-----+-----+-----+-----+-----+-+-----+-----+-----+-----
;	入力	    0,    1,    2,    3,    4,0,    6,    7,    8,    9
;		-----+-----+-----+-----+-----+-+-----+-----+-----+-----
	db	DribL,GDriL,DribL,ChgLR,GDriL,0,ChgLR,GDriL,DribL,ChgLR	; 0
	db	DribR,ChgRL,DribR,GDriR,ChgRL,0,GDriR,ChgRL,DribR,GDriR	; 2
	db	GDriL,GDriL,GDriL,LegLR,GDriL,0,LegLR,GDriL,GDriL,LegLR	; 4
	db	GDriR,LegRL,GDriR,GDriR,LegRL,0,GDriR,LegRL,GDriR,GDriR	; 6
	db	DribR,DribR,DribR,DribR,DribR,0,DribR,DribR,DribR,DribR	; 8
	db	DribL,DribL,DribL,DribL,DribL,0,DribL,DribL,DribL,DribL	;10
	db	DribR,DribR,DribR,DribR,DribR,0,DribR,DribR,DribR,DribR	;11
	db	DribL,DribL,DribL,DribL,DribL,0,DribL,DribL,DribL,DribL	;12
	db	DribL,DribL,DribL,DribL,DribL,0,BTnLR,DribL,DribL,BTnLR	;14
	db	DribR,DribR,DribR,DribR,BTnRL,0,DribR,BTnRL,DribR,DribR	;16
;		-----+-----+-----+-----+-----+-+-----+-----+-----+-----

;	--------------------------------------------
;	動作(アクション)番号→ポーズデータオフセット
;	--------------------------------------------
ACT	equ	48			;1ポーズ=48バイト
tableACT:
	dw	ACT*000,ACT*010		;ドリブルL/R
	dw	ACT*020,ACT*030		;ガード付きドリブルL/R
	dw	ACT*100,ACT*120		;チェンジLR/RL
	dw	ACT*140,ACT*160		;レッグスルーLR/RL
	dw	ACT*180,ACT*200		;バックターンLR/RL
	dw	ACT*100,ACT*120,ACT*140,ACT*160,ACT*180
	dw	ACT*200,ACT*220,ACT*240,ACT*260,ACT*280
	dw	ACT*300,ACT*320,ACT*340,ACT*360,ACT*380
	dw	ACT*400,ACT*420,ACT*440,ACT*460,ACT*480
------------------------------------------------------------

[戻る]


質問には答えます... koh@inetmie.or.jp