Coding Memorandum

プログラミングに関する備忘録

ラスタースクロール

この記事はMSX Advent Calendar 2015の7日目の記事です。

8bit,16bit CPU時代のゲーム機で流行ったゲームエフェクトにラスタースクロールというものがありました。水平走査線(画面の横1ピクセルライン)毎にスクロール位置を変えることで、画面がうねるような動きを表現します。ハード性能が向上すると、スクロールだけでなく拡大縮小のスケールを変えるなど、より凝った表現が行われるようになりました。(Google画像検索

ラスタースクロールについては、Wikipediaで非常に詳しく説明されています。

このラスタースクロールをMSXで実装してみました。ラスタースクロールの基本ロジックは、Wikipediaにもある通り次のようになります。

  1. ビデオチップが1ライン分の描画(VRAM読み込み)を終え、水平帰線期間(水平ブランキング期間)に入る。このとき、水平同期(HSYNC)信号が出される。
  2. CPUは、HSYNC信号で割り込み発生させる。
  3. プログラムは、HSYNC割り込み処理で横方向のスクロール位置を設定する(次に描画するラインのスクロール位置) 。
  4. 1~3の処理をスクロール位置を変えながら全ラインで実行する。

MSX2のビデオチップV9938では、コントロールレジスタ#19にHSYNC割り込みを発生させる走査線番号を指定することができます。これを使えばラスタースクロールを実現できそうですが、残念ながらこの方法では上手く行きません。

CPU(Z80)がHSYNC割り込みを受けると、アドレス038Hに実行場所が移ります。 ここから、MSXのシステムプログラムが実行されるため、ユーザプログラムに制御が移るまでにかなりの時間を要してしまいます。ユーザプログラムに制御が移るころには数ラインの描画が進行してしまい、スクロール位置の変更が間に合わない状況となります。

step3でスクロール位置設定を行った後に次の割り込み位置をR#19へ設定しますが、上述の状況ですので1ラインごとに割り込み処理を行わせることができません。こちらで試した結果では6ライン程の間隔で設定すれば動作させることができました。

1ラインのHSYNC毎にスクロール位置を変えるには、HSYNCを自前で検出する方法があります。水平帰線期間中は、V9938のステータスレジスタ#2のbit 5が1になります。このbitを監視して、HSYNCを検出することができます。(ただし、CPUを占有します)

これを実装したプログラムは次のようです。

TITLE 	Raster Scroll
	.Z80
	ASEG

RG0SAV	EQU	0F3DFH
RG18SA	EQU	0FFF1H
RG19SA	EQU	0FFF2H
RG25SA	EQU	0FFFAH
VDPIO	EQU	099H
HKEYI	EQU	0FD9AH

	ORG	09200H

START:
; Set IE1(Interrupt Enble1)
	DI
	LD	A,(RG0SAV)
	OR	010H
	LD	(RG0SAV), A
	OUT	(VDPIO),A
	LD	A,080H
	OUT	(VDPIO),A
; Set R19(Interrupt line register)
	LD	A,0
	LD	(RG19SA),A
	OUT	(VDPIO),A
	LD	A,093H
	OUT	(VDPIO),A
; Set R25
	LD	A,(RG25SA)
	OR	03H
	LD	(RG25SA),A
	OUT	(VDPIO),A
	LD	A,099H
	OUT	(VDPIO),A
; Set H.KEYI
	LD	HL,HKEYI
	LD	DE,HKEYISV
	LD	BC,5
	LDIR
	LD	HL,HKEYISET
	LD	DE,HKEYI
	LD	BC,5
	LDIR
	EI
	RET

ONKEYI:
	DI
	LD	C,VDPIO
	LD	H,093H
	LD	A,(L0OFFSET)
	INC	A
	LD	(L0OFFSET),A
	LD	L,A
	LD	B,0
; Read S1
	LD	A,01H
	OUT	(C),A
	LD	A,08FH
	OUT	(C),A
	IN	A,(C)
; Check FH Flag
	RRCA
	JR	NC,ENDKEYI
; Read S2
	LD	A,02H
	OUT	(C),A
	LD	A,08FH
	OUT	(C),A
; Wait HSYNC(HR:1->0->1)
L1:	IN	A,(C)
	AND	020H
	JR	NZ, L1
L2:	IN	A,(C)
	AND	020H
	JR	Z, L2

; Set Scroll => R18(Display adjust register)
	LD	A,1
	ADD	A,L
	AND	03FH
	LD	L,A
	LD	A,(HL)
	OUT	(VDPIO),A
	LD	A,092H
	OUT	(VDPIO),A
;
	INC	B
	LD	A, 211
	CP	B
	JR	C, ENDKEYI
	JR	L1

ENDKEYI:
	XOR	A
	OUT	(C),A
	LD	A,08FH
	OUT	(C),A
HKEYISV:
	DS	5
HKEYISET:
	JP	ONKEYI
	DS	2
L0OFFSET:
	DB	0FFH

	ORG	09300H
HSCROLL:
	DB	00H,01H,01H,02H,03H,03H,04H,04H,05H,05H,06H,06H,06H,07H,07H,07H
	DB	07H,07H,07H,07H,06H,06H,06H,05H,05H,04H,04H,03H,03H,02H,01H,01H
	DB	00H,0FH,0FH,0EH,0DH,0DH,0CH,0CH,0BH,0BH,0AH,0AH,0AH,09H,09H,09H
	DB	09H,09H,09H,09H,0AH,0AH,0AH,0BH,0BH,0CH,0CH,0DH,0DH,0EH,0FH,0FH

	END START

横方向のスクロール位置は、画面のAdjust機能(テレビ出力用に画面の位置を-8~+7の範囲で調整する機能)を用いています。 R#18に調整値を設定します。

実行結果は、次のようです。

画面の上部がズレているのは次の理由からです。Line 0のHSYNC割り込みを契機にラスタースクロールの処理を開始していますが、実際に処理が開始されるまで数ライン分の遅延があるため、ラスタースクロールの開始ラインが下にズレてしまったと考えられます。上部の数ラインは、一つ前のフレームの設定が反映されています。

ラスタースクロール開始の契機をVSYNC(垂直帰線期間)にすれば、このズレは抑えられると思います。プログラムを試行錯誤で作った名残&時間切れで、このような状態になっています。


MSX2+からは、ビデオチップ(V9958)で横方向スクロールをサポートしました。 ライン毎のスクロール位置指定に、このハードウェアスクロールを使ってみました。

ハードウェアスクロールは、R#26に8ピクセル単位のスクロール位置、R#27に1ピクセル単位のスクロール位置(0~7)を指定します。 残念ながらR#27を指定すると、画面が乱れてしまいました。これは、今回使ったエミュレータの問題か、V9958の性能面での問題かは分かっていません。

R#26のみ設定した場合は、期待通りに動作しました。実行画面は次のようになります。

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://msirocoder.blog35.fc2.com/tb.php/93-f41412a6
この記事にトラックバックする(FC2ブログユーザー)