macOS でゲームボーイ用ゲームの開発環境を構築する

Game, C, GameBoy

ゲームボーイのゲームを作りたかったのでやった。
あと今のキーボードが Windows だと使いづらいので、開発体験的な意味でも全てを Mac で完結させたかった。

コーディングからカートリッジ書き込みまで、全て Mac でやるのが目標。

ゲームボーイのゲーム開発

ゲームボーイのゲーム開発には RGBDSGBDK-2020 どちらかを用意することになると思う。
RGBDS はゲームボーイ開発用のツールチェーン。ASM で書かれたものをアセンブルする。
GBDK は ASM だけでなく C が書けるようになっているので、習得がかなり楽。
あとは WLA-DX というのがあって、そちらは GB や Z80 以外もサポートしているらしい。

ASM はゲームボーイ本体の知識が必要。C はイージーだけど ASM に比べればパフォーマンスが劣る。
とりあえず C を書きながら、少しずつ ASM も書けるように頑張っていく。

他に良いツールとしては GB Studio がある。Electron 製の GUI でゲームボーイのゲームが作れる。お行儀の悪いバグゲーを作れなさそうなので泣く泣く候補から外した。

環境、用意したもの

インテル製及び Apple Silicon(M1) モデルの MacBook で動作を確認した。

  • macOS Big Sur 11.4
  • GNU Make 3.81
  • rgbds-0.5.1
  • gbdk-4.0.4
  • Apple clang version 12.0.5 (clang-1205.0.22.11)
  • wine-5 CrossOver(GBTD-GBMB を使う場合)
  • gtk 2.24.33(ppm を使う場合)
  • Qt 6.1
  • libftdi 1.5

インストール&コンパイル(GBDK の場合)

GBDK のリポジトリから macOS のバイナリをダウンロードして解凍する。
解凍してできた gbdk フォルダの中に hello-gbdk フォルダを作る。
そのフォルダの中に main.c を置く。

gbdk/hello-gbdk/main.c

#include <gb/gb.h>
#include <stdio.h>

void main()
{
  printf("HELLO WORLD");
}

そして Makefile を置く。

gbdk/hello-gbdk/Makefile

CC	= ../bin/lcc

BINS	= main.gb

all:	$(BINS)

%.gb:	%.c
	$(CC) -o $@ $<

make を実行する。

shell

cd gbdk/hello-gbdk
make

hello-gbdk フォルダに main.gb ができる。

インストール&コンパイル(RGBDS の場合)

好きなところに hello-rgbds フォルダを作る。
その後 brew で rgbds をインストールする。

shell

brew install rgbds

まず、ハードウェアのファイルを用意しておく。GB Dev にある hardware.inc を、hello-rgbds フォルダに入れれば OK。

次に、ASM を書く。GB ASM Tutorial の hello-world.asm を少しいじって main.asm を作った。これも hello-rgbds フォルダに入れる。
(コードのライセンスは CC0 ですが、オリジナルのコードは GB ASM Tutorial のものであることをここに示します。タイルマップの部分などを変更しています)

hello-rgbds/main.asm

INCLUDE "hardware.inc"

SECTION "Header", ROM0[$100]
 	nop
	jp main
	ds $150 - @, 0

main:
	di
	ld a, 0
	ld [rNR52], a

WaitVBlank:
	ld a, [rLY]
	cp 144
	jp nz, WaitVBlank

	ld a, 0
	ld [rLCDC], a

	ld de, Tiles
	ld hl, $9000
	ld bc, TilesEnd - Tiles

CopyTiles:
	ld a, [de]
	ld [hli], a
	inc de
	dec bc
	ld a, b
	or a, c
	jp nz, CopyTiles

	; Copy the tilemap
	ld de, Tilemap
	ld hl, $9800
	ld bc, TilemapEnd - Tilemap

CopyTilemap:
	ld a, [de]
	ld [hli], a
	inc de
	dec bc
	ld a, b
	or a, c
	jp nz, CopyTilemap

	ld a, LCDCF_ON | LCDCF_BGON
	ld [rLCDC], a
	ld a, %11100100

	ld [rBGP], a

Done: 
	jp Done

SECTION "Tile data", ROM0

Tiles:
	db $00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00
	db $66,$66,$66,$66,$66,$66,$7e,$7e
	db $66,$66,$66,$66,$66,$66,$66,$66
	db $7f,$7f,$60,$60,$60,$60,$7e,$7e
	db $60,$60,$60,$60,$60,$60,$7f,$7f
	db $60,$60,$60,$60,$60,$60,$60,$60
	db $60,$60,$60,$60,$60,$60,$7e,$7e
	db $7e,$7e,$c3,$c3,$c3,$c3,$c3,$c3
	db $c3,$c3,$c3,$c3,$c3,$c3,$7e,$7e
	db $83,$83,$83,$83,$b3,$b3,$b3,$b3
	db $b3,$b3,$b3,$b3,$b3,$b3,$fc,$fc
	db $7c,$7c,$62,$62,$62,$62,$7c,$7c
	db $66,$66,$66,$66,$66,$66,$66,$66
	db $7c,$7c,$66,$66,$62,$62,$62,$62
	db $62,$62,$62,$62,$66,$66,$7c,$7c
TilesEnd:

SECTION "Tilemap", ROM0

;ウィンドウ表示がうまくいかなかったので一旦バックグラウンドで埋めている
Tilemap:
	db $01,$02,$03,$03,$04,$00,$05,$04,$06,$03,
	db $07,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
TilemapEnd:

hello-rgbds フォルダに Makefile を置く。

hello-rgbds/Makefile

all:	build

build:
	# アセンブル
	@rgbasm -L -o main.o main.asm

	# リンク
	@rgblink -o main.gb main.o

	# -v -p 0xFF でヘッダと ROM サイズを正しく修正してくれる
	@rgbfix -v -p 0xFF main.gb

make を実行する。

shell

cd gbdk/hello-rgbds
make

hello-rgbds フォルダに main.gb ができる。

スクリーンショット

hello-gbdk でコンパイルしたやつ。
フォントは printf で出力されるもの。

GBDK でコンパイルしたゲームボーイの HELLO WORLD を出すプログラムが動いているところ

RGBDS のやつ。
フォントは自前で用意したやつなので GBDK のものとは表示が異なる。

RGBDS でコンパイルしたゲームボーイの HELLO WORLD を出すプログラムが動いているところ

ツールを入れる

ASM や C だけで開発するのは厳しいので、ツールを導入する。
今回はこんなツールを使うんですよ、というさわりの紹介なので詳細には触れない。

エミュレーター

gb ファイルは、GB カートリッジに焼くか、エミュレーターを使用して起動できる。
開発中はデバッグも兼ねて、機能が充実したエミュレーターを使用することが多い。

GB のエミュレーターは BGB が有名。再現性が高く、残像の再現が可能で、デバッグ機能が充実している。
ただ BGB は macOS に対応していないので Wine で動かす必要がある。Wine については後述する。

macOS 向けのアプリケーションとしては、メモリと VRAM のチェックがそこそこにできる SameBoy がある。今回は SameBoy をメインで使ってみることにした。

タイルエディター、マップエディター

スプライトや背景の作成にタイルエディター、レベルデザインをするためのマップエディターが必要。
gbdk-2020/GBTD_GBMB を使うか、ppm を使う方法 2 種類がある。

まずポピュラーな gbdk-2020/GBTD_GBMB は、動作に Wine が必須。
Wine は Linux や Mac の環境で Windows アプリケーションを動かす互換レイヤー。
Wine 自体は 32bit なので、Catalina 以降の OS で動かない。現状 cross-over と言うのが 64bit になっているので、こちらの記事を参考に導入する。

GBMBというツールを使って、ステージのマップを作成している

次に、ppm というタイル & マップエディターがあり、そちらをビルドできる。
gtk2 が必要だけど、こちらは Wine なしで起動できる。

shell

brew install gtk+
cd ppm-master
make
sudo make install

使い方はよくわかっていない。
ウィンドウが小さいから、改造が必要かも。

ppm でタイルエディターとマップエディターを開いている

作曲・mod変換ツール

.mod を書き出せるトラッカーツールを使って、gbt-player の mod2gbt で ASM に変換できる。
それを gbt-player を使って鳴らす。

トラッカーは openMPT が定番らしい。でもどうせまた Wine なんでしょう、と言われたら悔しいので、macOS に対応した MilkyTracker というアプリケーションを使う。
UI のクセが強いがなんとか使える。ふいんきでやってる。

milkytrackerでゲームボーイのBGMとなるトラックを作成している

変換や再生を担う mod2gbt や gbt-player は、GBDK 向けでのメンテはされていない。
GBDK に依存する GB Studio は、少し前の 2.1 を使用してビルドしているようで、そちらのバージョンで試したところ変換できた。
一応 GBDK でも使えるだけであって、あくまで ASM 向けであることは意識しておかないといけない。

効果音ツール

ダウンロードした GBDK にはいくつか examples があり、その中の gbdk/examples/gb/sound/ をビルドすると、sound.gb が生成される。
これはサウンド再生のシミュレーターだけど、レジスタに対応した値を表示する便利な機能がある。
たとえば、sound.gb で「ブヨ〜ん」みたいな音を作り、画面下の NR10-14 の値が 17, 8C, 43, 41, 86 になっている時…。

sound.gb でパラメーターをいじってブヨ〜んという音を作った。NR10-14 という各レジスタに対応した値が表示されている

GBDK では NR10〜NR14 をこのように記述することになる。

sample.c

NR10_REG = 0x17;
NR11_REG = 0x8C;
NR12_REG = 0x43;
NR13_REG = 0x41;
NR14_REG = 0x86;

フラッシャーとカートリッジ

開発したゲームを共有したりブラウザ上で動かしてもいいけど、カートリッジに焼かないとやった感が出ない。
CUBIC STYLE さんが BOOTH にてフラッシャーを販売しているので、そちらを購入した。
組み立て済みのフラッシャー本体と、32kb のフラッシュカートリッジがついてくる。
パッケージに惹かれたのが一番のところ。これは初代 GB ソフトのパッケージと同じサイズでもある。

CUBIC STYLEの通販で購入したゲームボーイのフラッシャーと、32kbのカートリッジが入ったパッケージ

注意点は gbt-player を使用すると、データがバンク 1 に入るので 64kb の ROM ができること。
今回は無理やり詰め込んで 32kb に押し込めた。やり方は後日機会があれば書く。

書き込みアプリケーション

書き込み用ソフトも CUBIC STYLE さんが配布しているけど、 Windows のみの対応。ただフォーク元の gbcartflasher 自体は Qt 製で macOS でも対応している。
それで、CUBIC STYLE さんのものをフォークして、こちらのリポジトリで Qt6 向けソースを作成した。
リポジトリから zip をダウンロードして解凍、中の gbcflsh_1.1_libftdi フォルダに移動。
Qt と libftdi を入れて、qmake && make すれば gbcartflasher-master/gbcflsh_1.1_libftdi/release に app ファイルができる。

shell

brew install qt libftdi
cd gbcartflasher-master/gbcflsh_1.1_libftdi
qmake && make 

次に、フラッシャーを Mac に USB 接続する。
ターミナルで system_profiler SPUSBDataType を実行し、FT232R USB UART が認識されていれば OK。
ついでに PID は 0x6001 VID は 0x0403 になっているか確認。
認識されていない場合は、VCP ドライバがいるかもしれない。

shell

FT232R USB UART:

  Product ID: 0x6001
  Vendor ID: 0x0403  (Future Technology Devices International Limited)
	// 省略

これでゲームの読み書きができる。

ゲームボーイのカートリッジに自作ゲームを書き込んでいる

終わり

これで macOS での GB ゲーム開発環境が整った。
今は「たこ焼きくん」という習作で色々とやってる。
ハードオフのジャンク箱で 108 円で売られてそうなアクションパズルを目指している。

ゲームボーイで、たこ焼きくんという自作ゲームを動かしている