0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ゲームギアでZ80プログラミング

0
Posted at

昔触っていたZ80アセンブラで何か作りたいなと思っていたところ以下の記事を発見!
コピペで遊ぼう レトロゲーム プログラミング ~ 1. セガ ゲームギアでHello World!

いろいろ触ってみることにしました。
とはいえゲームギアは触ったことはありません。
上記記事からゲームギアのハードウェア仕様書などを参考にしました。

画面表示構成を見てみると、BG(back ground)を表示する部分はPattern name table、BG表示するためのデータを置くところがPattern generator table、Pattern generator tableの約半分を共用する形でSprite generator tableを使用してSprite attribute tableを使ってスプライトを表示する仕組み。

Pattern generator tableの構成はパレット番号を組み合わせていてちょっとめんどくさい構造。パレットを白黒の2色だけにすると少しは簡単にできそう。

そんなわけで、Pattern name tableとPattern generator tableを1対1になるよう構成させて1ドットを置くプログラムを作成してみました。

上記記事のコードにおいてフォントデータが大きいので数字とアルファベット大文字だけ使うように64個分残した後を画面表示用に使います。

フォントデータをセットした後で以下を組み込み。

;-------------------------------------------------------------------------------
;name tableとpattern generatorを1対1で紐づけ

	ld	HL, $38cc | VRAMWrite	;32*2*3+6*2=204=$cc + $3800(name table)
	ld	DE, $40					;font64個=$40(pattern generator number + font)
	ld	C,18					;表示Y方向最大値
set_loop2:
	push	HL
	call	SetVDPAddress
	
	push	DE					;DE→HL
	pop	HL						;pattern generator address set
	ld	B, 20					;表示X方向最大値
set_loop1:
	ld	A, L
	out	(VDPData), A
	ld	A, H
	out	(VDPData), A
	
	ld	DE, 1					;pattern generator number + 1
	add	HL, DE					;next pattern generator address
	djnz	set_loop1

	push	HL					;HL→DE
	pop	DE						;pattern generator address update

	pop	HL

	push	DE
	ld	DE, $40					;32*2(次の行へ)
	add	HL, DE					;name tableの次の行へ
	pop	DE

	dec	C
	jr	nz, set_loop2

画面に1ドットを置くプログラムは以下

;-------------------------------------------------------------------------------
;表示画面に点を置く
;B=X座標(0-159), C=Y座標(0-143)
;point_flag = 0(set:白セット), 1(clear:黒セット)
PointSet:
	push	HL
	push	DE
	push	BC

	ld	HL, 0

	ld	A, C
	srl	A				;÷2
	srl	A				;÷4
	srl	A				;÷8
	jr	z, ps_0
	ld	DE, 20
ps_1:
	add	HL, DE
	dec	A
	jr	nz, ps_1
ps_0:
	ld	A, B
	srl	A				;÷2
	srl	A				;÷4
	srl	A				;÷8
	ld	E, A
	ld	D, 0
	add	HL, DE				;HL = Y*20+X
	
	push	HL				;HL退避[1]
	
	sla	L				;x2
	rl	H
	sla	L				;x4
	rl	H
	sla	L				;x8
	rl	H				;HL = (Y*20+X)*8
	
	ld	DE, DISP_RAM
	add	HL, DE

	push	BC				;BC退避[1]
	
	ld	A, C
	and	%00000111
	ld	E, A
	ld	D, 0
	add	HL, DE				;DISP_RAMアドレス


	ld	A, B
	and	%00000111			;bit番号

	;bit番号を値に変換(bit0=$80,bit1=$40,,,,bit7=$01)
	jr	z, ps_6
	ld	B, A
	ld	A, $80
ps_7:
	srl	A
	dec	B
	jr	nz, ps_7
	jr	ps_8
ps_6:
	ld	A, $80
ps_8:
	push	AF				;AF退避[1]

	ld	A,(point_flag)
	bit	0, A				;0 or 1 判定
	jr	nz, ps_4

	;SET
	pop	AF					;AF復元[1]
	or	(HL)				;bit set
	jr	ps_5
ps_4;
	;CLEAR
	pop	AF					;AF復元[1]
	ld	E, A				;E←A(data)
	ld	A, $ff
	sub	E					;A←($ff-data)
	and	(HL)				;andでbit clear
ps_5:
	ld	(HL), A				;DISP_RAM更新

	ld	(temp_data), A		;編集データ仮保存
	
	;該当する Name Table の Pattern Generator Number を取得
	;そのPattern Generator Addressを編集する

	;Number = 20*Y+X+$40
	;Address = Number * 32
	
	pop	BC					;BC復元[1]
	pop	HL					;HL復元[1]
	
	ld	DE, $40				;FontData分加算
	add	HL, DE
	
	sla	L					;x2
	rl	H
	sla	L					;x4
	rl	H
	sla	L					;x8
	rl	H
	sla	L					;x$10
	rl	H
	sla	L					;x$20
	rl	H					;HL = (Y*20+X+$40)*32	Gen Address

	ld	A, C
	and	%00000111
	jr	z, ps_2
	
	ld	DE, 4				;セル内Y位置オフセット加算
ps_3:
	ADD	HL, DE				;Gen編集該当アドレス
	dec	A
	jr	nz, ps_3
ps_2:
	ld	DE, VRAMWrite
	add	HL, DE
	call	SetVDPAddress

	ld	A, (temp_data)
	out	(VDPData), A		;VRAM Write

	pop	BC
	pop	DE
	pop	HL

	ret

RAMに以下を設定

.equ	point_flag	$c000		;b
.equ	temp_data	$c001		;b
.equ	DISP_RAM	$c100		;20*18*8=2880=$b40

Pattern generator tableの読み出しができなかったので、RAM上にPattern generator tableに設置するデータ(DISP_RAM)を置いて、そのデータを編集してPattern generator tableに書き込みしています。

Z80プログラミングも久しぶりだしGameGearの仕組みも完全に理解したわけではないのでプログラミングコードはあくまで参考までに・・・

mainloopに以下を置く。

;-------------------------------------------------------------------------------
;埋めてみる

	ld	A, 0
	ld	(point_flag), A			;SET

	ld	C, 144
gm_2:
	ld	B, 160
gm_1:
	push	BC
	dec	B			;decしてゼロで終了するので+1~+160, +1~+144でセットする
	dec	C
	call	PointSet
	pop	BC
	dec	B
	jr	nz, gm_1
	dec	C
	jr	nz, gm_2
-	jr	-			;STOP

画面全部を埋めてみました。

すると、ドット抜けが発生してしまいます。
待ち無しベタ塗り結果.png
VDPの準備待ちが必要そうなので以下に修正。

;-------------------------------------------------------------------------------
;埋めてみる

	ld	A, 0
	ld	(point_flag), A			;SET

	ld	C, 144
gm_2:
	ld	B, 160
gm_1:
	;----------
	ld	A, B
	and	1
	jr	nz, gm_3
	;2回に1回VDPの準備待ちを通す(3回以上では抜けが発生する)
	;---
	;VDPの準備待ち
	call	VDP_ready_wait
gm_3:
	;----------
	push	BC
	dec	B			;decしてゼロで終了するので+1~+160, +1~+144でセットする
	dec	C
	call	PointSet
	pop	BC
	dec	B
	jr	nz, gm_1
	dec	C
	jr	nz, gm_2
-	jr	-			;STOP

待ちありベタ塗り結果.png
成功しました、、、が描画完了するのにかなり時間がかかってしまいます。
遊ぶだけなら抜けても問題ないかなと思います。

ついでに乱数発生器を作成しました。

;----------------------------------------------------------------------
; random data get
;	ret : A reg. (and random_data)
;----------------------------------------------------------------------
random_get:
	push	HL
	add	HL, SP
	add	A, H
	add	A, L
	ld	L, A
	ld	A, R
	add	A, L
	ld	HL, random_data
	add	A, (HL)
	add	A, (HL)
	add	A, (HL)
	rlca	
	ld	(HL), A
	inc	HL
	add	A, (HL)
	add	A, B
	add	A, C
	add	A, D
	add	A, E
	rlca	
	ld	(HL), A
	pop	HL
	ret

ランダムシードをRAMに設置

.equ	random_data	$c002		;b

mainloopに以下のコードを置いて乱数の分布を見てみます。

;-------------------------------------------------------------------------------
;ランダムPointSet
	ld	A, 0
	ld	(point_flag), A			;SET
	call	random_get
	cp	159						;範囲外チェック
	jr	nc, gm_0
	ld	B, A
	call	random_get
	cp	143						;範囲外チェック
	jr	nc, gm_0
	ld	C, A
	call	PointSet
gm_0:
	ret

1時間くらい放置すると
Z80ランダム結果.png
良い結果が得られました。

ドット置くコードができたのでここからいろいろ遊んでみようかと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?