ゲームボーイで画像を表示した話

昨日の続きでゲームボーイ(以下GB)のお話。

画像を表示したい(BG編)

GBにも任天堂ハードの例によってBGとOBJ(加えてGBにはウインドウ)があります。ざっくり言えばOBJはスプライト(プレイヤーや敵キャラみたいな動き回るやつ)でBGは背景(タイル状に敷き詰められてオフセットスクロールのみ)です。ウインドウはまだ詳しく調べてないけど、BGとは別に(=オフセットスクロールに依存しない)サブ画面を持たせるためのやつらしい。ファミコンよりゲーム用の機能を充実させた感じ。
今回は任意の画像をGBのBG画面に表示させたいと思います。ところで「GBのBG画面」ってややこしいな。

まずGBDKのコンパイラのインクルードファイルを眺めます。/opt/gbdk/include 以下がそれですね。
gb/hardware.h とか gb/gb.h あたりにいい感じの定義とか関数がたくさんまとめられてるっぽい。gb/gb.hを見てみましょう。

(略)
/** Turns on the background layer.
    Sets bit 0 of the LCDC register to 1.
*/
#define SHOW_BKG \
  LCDC_REG|=0x01U
(略)

LCDC_REG はBGコントロールレジスタへのポインタっぽいです。SHOW_BKG;でBGを有効化できるみたいですね。

続けて下の方も見てみましょう。

(略)
/** Sets the tile patterns in the Background Tile Pattern table.
    Starting with the tile pattern x and carrying on for n number of
    tile patterns.Taking the values starting from the pointer
    data. Note that patterns 128-255 overlap with patterns 128-255
    of the sprite Tile Pattern table.  

    GBC: Depending on the VBK_REG this determines which bank of
    Background tile patterns are written to. VBK_REG=0 indicates the
    first bank, and VBK_REG=1 indicates the second.

    @param first_tile   Range 0 - 255
    @param nb_tiles Range 0 - 255
*/
void
set_bkg_data(UINT8 first_tile,
         UINT8 nb_tiles,
         unsigned char *data) NONBANKED;
(略)

void set_bkg_data() はタイルの画像データを配列 *data で受け取ってVRAMに設定してくれそうな感じがします。

(略)
/** Sets the tiles in the background tile table.
    Starting at position x,y in tiles and writing across for w tiles
    and down for h tiles. Taking the values starting from the pointer
    data.

    For the GBC, also see the pan/k00Pa section on VBK_REG.

    @param x        Range 0 - 31
    @param y        Range 0 - 31
    @param w        Range 0 - 31
    @param h        Range 0 - 31
    @param data     Pointer to an unsigned char. Usually the 
                first element in an array.
*/
void
set_bkg_tiles(UINT8 x,
          UINT8 y,
          UINT8 w,
          UINT8 h,
          unsigned char *tiles) NONBANKED;
(略)

こっちの void set_bkg_tiles() はタイルのパターン(配置)を配列 *tiles で受け取ってVRAMに設定してくれそうな感じがします。

ここで昨日紹介したGBTDG(GAMEBOY Tile Data Generator) を見ると、ちょうど画像から画像データとそのタイルパターンの配列を出力してくれる(しかも同じタイルを最適化してくれる、神)じゃないですか!
ゲームボーイの解像度は160x144、色数はモノクロ4階調なので、それに合わせて画像を作成して、GBTDGに投げて出力を保存します(ここではshumatsu.cとしましょう)。

上のgb/gb.hを参考に、昨日のHello Worldのmygame.cを以下の内容に書き換えます。

#include <gb/gb.h>
#include "shumatsu.c"

void main() {
    set_bkg_data(0, 255, shumatsu_tile_data);
    set_bkg_tiles(0, 0, shumatsu_tile_map_width, shumatsu_tile_map_height, shumatsu_map_data);

    SHOW_BKG;
}

コンパイルされたmygame.gbをエミュレータで開いて、あらかじめ作成した画像が表示されれば成功です。やったぜ。

f:id:yaplus:20180603180412p:plain
終わるまでは終わらないよ

set_bkg_tilesし忘れるとこうなります。(追記:この形状的に起動直後のNintendoロゴの残骸っぽいと思うんだけどよくわからん。VRAM見れるエミュレータが必要だな…。)

f:id:yaplus:20180603180445p:plain
石でも食ってろ

こんなんサルでもできるやろがい

f:id:yaplus:20180603180556p:plain
エーケイザー・エケリ

GBAの時は参考書籍を参考にドライバを自作したので、インクルードひとつでこれだけの機能が扱えるあたり、先哲の知恵は偉大ですね。
まあ実際には貧弱なリファレンスとインターネットの怪情報を片手にGBDKにどういう機能が用意されているのか調べる必要があるし、コンパクトなソースにしたいのであれば自分で実装した方が早いというのもあるかもれないですけども、今回は車輪の再発明をしないという方向でやっていきたいと思います。英語を読む勉強にもなるし。

色々と気になったことがあったので以下は備忘録です。

ROMの拡張(バンク切り替え)ってどのくらいの規模になると必要になるの?

こんなもの見つけました。GBのソフトとその容量およびカセット内部の構成をまとめたものです。
とりあえず思いつくゲームタイトルで検索してみると、なんとGBのローンチタイトルであるスーパーマリオランドですらいきなりMBC1によるバンク切り替え(64KB)を行なっているようです(!)。ファミコンでのノウハウから最初からバンク切り替えを前提に設計されたと思っていいでしょう。実際MBC1にはMMCに似たところがあるようです(未確認)。
一方でテトリス(無印)やドクターマリオヨッシーのたまごあたりの初期のパズルゲームタイトルはバンク切り替えなし(32KB)です。ハードは異なりますが、以前自作したガヴドロアドバンスの容量は90KBくらいで、しかもGBAは複数BG分タイルデータが大きいにも関わらずこの程度で済んでるので、自作ソフトでどれだけ作り込むかにもよりますが、案外ミニゲーム程度であれば32KBでもなんとかなるんじゃないかと思えてきました。

万一容量がオーバーするような場合にはGBDKは手動でのバンク切り替えをライブラリでサポートしてくれるみたいですGBDKのドキュメントは色々お役立ち情報があるみたいですがたまに書きかけで雑に放置されてたりするので過信は禁物。

OBJ(スプライト)のパレットってどうなってるの?透明色の設定は?

GBはファミコンと同じく1つのOBJに対して3色+透明色1色を設定できます。扱える色は4色なので、4色のうち1色が透明色という扱いになるみたい。
具体的には

OBP0_REG = 0xE1;

みたいに設定します。
GBでは2ビット(4色)が11が黒、10が濃いグレー、01が薄いグレー、00が白と設定されています。
0xE1 = 0b11100001であり、OBP0_REGの2ビットずつがパレットであり、最下位2ビットが透明色に設定されるということらしいです。この場合は01(薄いグレー)が透明色になります。

当面の目標

OBJ(スプライト)の表示

キー入力の受け付け

これがないとゲームにならないでしょ。
やることとしては当面GBA開発のときと同じ手順でやっていくつもり。

感想

うるせ〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜!!!!!!!
しらね〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜!!!!!!!!

魔界塔士Sa・
Ga