Tutorial.10 タイルモード(1)

タイルモードはmode0~2に該当します。モード0の動作画面から見てみましょう。なお結構難しいと思うので何日か分けて取り組んでみてください。

Mode名称
0-2タイルモード
3-5ビットマップモード
1.png

何やらRPGの洞窟風景に見えますね。これらを表示するには以下の画像データを使います。

2.png

8x8ドットを1つの単位としてタイルのように並べて実現します。画像の場合だと8x8ドットだと小さすぎるので16x16ドット、4つ分を塊にしています。種類としては砂、沼、床、壁の4つです。洞窟風景を実現するにはパレットデータ、キャラクタデータ、マップデータの3つが必要です。さっそく、それぞれを見ていきましょう。

パレットデータ

Color Palette RAM
BG and OBJ palettes are using separate memory regions:

05000000-050001FF - BG  Palette RAM (512 bytes, 256 colors)
05000200-050003FF - OBJ Palette RAM (512 bytes, 256 colors)

Color Definitions
Each color occupies two bytes (same as for 32768 color BG modes):

Bit   Expl.
0-4   Red Intensity   (0-31)
5-9   Green Intensity (0-31)
10-14 Blue Intensity  (0-31)
15    Not used

スプライトで説明した表を再度引用しました。手抜きではありません(汗。今回はBG Palette RAMがターゲットとなります。

キャラクタデータ

ドット単位の格納方法サイズ
16横2ドット=1byte(下位4bitが右、上位4bitが左)32byte
256横1ドット=1byte64byte

こちらもスプライトの時に使った表と同じです。単位は1キャラクタ8x8ドットです。たとえばレンガ(16x16ドット)のキャラクタ番号は、6, 7, 14, 15となっています。

67
1415

マップデータ

キャラクタデータの並び方を決めたものがマップデータです。
2バイト(16ビット)単位であり、少々のオプションも含まれています。

Bit   Expl.
0-9   Tile Number     (0-1023) (a bit less in 256 color mode, because
                         there'd be otherwise no room for the bg map)
10    Horizontal Flip (0=Normal, 1=Mirrored)
11    Vertical Flip   (0=Normal, 1=Mirrored)
12-15 Palette Number  (0-15)    (Not used in 256 color/1 palette mode)

Tile Numberと書かれているところにキャラクタ番号を使います。
データについては自前に用意したものを使います。

#define BG0_MAP_SIZE (32*32*2)		// 2048バイト

const unsigned short ResBg0Map[] = {

6,7,6,7,6,6,6,6,
6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,

14,15,14,15,6,6,6,6,
6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,

(以下略・・・)

gritによる加工

さて、今回は慎重に「モード0 256色タイル」のオプションと出力結果を見てみましょう。

キャラクタデータは256色のため、64*16*1 = 1024バイト。
パレットデータも同様に256色のため、256*2 = 512バイトとなっています。

格納領域を決める

スプライトの場合、キャラクタとマップデータを格納する場所は
固定されおり、単純なコピーで済んでいました。

ところでタイルモードは可変であり、自分で格納する場所を
指定しなくてはいけません。対応表を以下に表します。

マップは0~31、キャラクタは0~3が指定可能です。メモリ領域の重複は許されません。
たとえばマップ16、キャラクタ2のようにしてはいけません。

u32 tile = 0;
u32 map  = 8;

REG_DISPCNT = (MODE_0 | BG0_ON);
REG_BG0CNT  = (BG_SIZE_0 | BG_256_COLOR | ((tile) << 2) | ((map) << 8) | 0);

今回はこのようにGBAの設定を行っています。つまり
タイルデータは0x6000000から格納し、マップデータは0x6004000から格納します。

REG_BGxCNT

REG_BGxCNTの内容をGBATEKから抜粋してみましょう。

4000008h - BG0CNT - BG0 Control (R/W) (BG Modes 0,1 only)
400000Ah - BG1CNT - BG1 Control (R/W) (BG Modes 0,1 only)
400000Ch - BG2CNT - BG2 Control (R/W) (BG Modes 0,1,2 only)
400000Eh - BG3CNT - BG3 Control (R/W) (BG Modes 0,2 only)

 Bit   Expl.
 0-1   BG Priority           (0-3, 0=Highest)
 2-3   Character Base Block  (0-3, in units of 16 KBytes) (=BG Tile Data)
 4-5   Not used (must be zero)
 6     Mosaic                (0=Disable, 1=Enable)
 7     Colors/Palettes       (0=16/16, 1=256/1)
 8-12  Screen Base Block     (0-31, in units of 2 KBytes) (=BG Map Data)
 13    Display Area Overflow (0=Transparent, 1=Wraparound; BG2CNT/BG3CNT only)
 14-15 Screen Size (0-3)

Internal Screen Size (dots) and size of BG Map (bytes):

 Value  Text Mode      Rotation/Scaling Mode
 0      256x256 (2K)   128x128   (256 bytes)
 1      512x256 (4K)   256x256   (1K)
 2      256x512 (4K)   512x512   (4K)
 3      512x512 (8K)   1024x1024 (16K)

In case that some or all BGs are set to same priority then BG0 is having the highest,
and BG3 the lowest priority.

BG0~3というのは画面(back ground)の意味です。
なぜ可変として面倒なことをしていたか、理由はここにあります。

GBA(モード0)はマップデータを最大4つ持つことができ、
重ね合わせ表示が可能になっています。また、14-15ビット目を見ると
256x256~512x512ドットに変えることも可能です。

先ほどの対応表の区切りをもう一度見てください。
マップは2K=2048(0x800)バイト単位でした。512x512をしたい場合は8Kとなり、
区切り4つ分が必要になります。

モード0で、BG0のみを使用する

覚えることが大量に出てきたところでいったんクールダウンしましょう。
とりあえず先ほどのRPGの洞窟風景のコードを以下に表します。

#include "lib/gba.h"
#include "res.h"

#define BG_MAX_CNT 4

typedef struct {
	u32  mapBase;
	u16* mapBaseAdr;
	u32  tileBase;
	u16* tileBaseAdr;
} ST_BG;

//---------------------------------------------------------------------------
ST_BG Bg[BG_MAX_CNT];

//---------------------------------------------------------------------------
void WaitForVsync(void)
{
	while(*(vu16*)0x4000006 >= 160) {};
	while(*(vu16*)0x4000006 <  160) {};
}
//---------------------------------------------------------------------------
void BgInitMem(void)
{
	const u32 mapBase[]  = {  8,  9, 10, 11 };
	const u32 tileBase[] = {  0,  0,  0,  0 };
	s32 i;

	for(i=0; i<BG_MAX_CNT; i++)
	{
		Bg[i].mapBase     = MAP_BASE(mapBase[i]);
		Bg[i].mapBaseAdr  = MAP_BASE_ADR(mapBase[i]);
		Bg[i].tileBase    = TILE_BASE(tileBase[i]);
		Bg[i].tileBaseAdr = TILE_BASE_ADR(tileBase[i]);
	}

	for(i=0; i<32*32; i++)
	{
		Bg[0].mapBaseAdr[i] = 0;
		Bg[1].mapBaseAdr[i] = 0;
		Bg[2].mapBaseAdr[i] = 0;
		Bg[3].mapBaseAdr[i] = 0;
	}

	for(i=0; i<0x2000; i++)
	{
		Bg[0].tileBaseAdr[i] = 0;
		Bg[1].tileBaseAdr[i] = 0;
		Bg[2].tileBaseAdr[i] = 0;
		Bg[3].tileBaseAdr[i] = 0;
	}
}
//---------------------------------------------------------------------------
void BgInit(void)
{
	BgInitMem();

	REG_DISPCNT = (MODE_0 | BG0_ON);
	REG_BG0CNT  = (BG_SIZE_0 | BG_256_COLOR | Bg[0].tileBase | Bg[0].mapBase | 0);
}
//---------------------------------------------------------------------------
void Bg0SetTile(u16* pDat, u32 size)
{
	u32 i;

	for(i=0; i<size; i++)
	{
		Bg[0].tileBaseAdr[i] = pDat[i];
	}
}
//---------------------------------------------------------------------------
void Bg0SetPal(u16* pDat)
{
	u32 i;

	for(i=0; i<256; i++)
	{
		BG_PALETTE[i]  = pDat[i];
	}
}
//---------------------------------------------------------------------------
void Bg0SetMap(u16* pDat, u32 size)
{
	u32 i;

	for(i=0; i<size; i++)
	{
		Bg[0].mapBaseAdr[i] = pDat[i];
	}
}
//---------------------------------------------------------------------------
int main(void)
{
	BgInit();

	Bg0SetTile((u16*)&bg0Tiles, bg0TilesLen/2);
	Bg0SetPal ((u16*)&bg0Pal);
	Bg0SetMap ((u16*)&ResBg0Map, BG0_MAP_SIZE/2);

	u32 x = 0;
	u32 y = 0;

	for(;;)
	{
		WaitForVsync();

		if( !(REG_KEYINPUT & KEY_UP)   ) y--;
		if( !(REG_KEYINPUT & KEY_DOWN) ) y++;
		if( !(REG_KEYINPUT & KEY_LEFT) ) x--;
		if( !(REG_KEYINPUT & KEY_RIGHT)) x++;

		x = x & 0xff;
		y = y & 0xff;

		REG_BG0HOFS = x;
		REG_BG0VOFS = y;
	}
}

REG_BG0HOFS、REG_BG0VOFSは何なのかちょっと気になるところです。
これは256x256のマップサイズから、GBAの液晶サイズ240x160分の始点を
どこにするか決めるものです。

ぜひエミュレータで実行してみてください。

動作画面とMap View

#ref(): File not found: "clip_1.png" at page "tutorial.10"

#ref(): File not found: "clip_3.png" at page "tutorial.10"

履歴


トップ   一覧 検索 最終更新   ヘルプ   最終更新のRSS