#author("2023-04-26T17:24:15+09:00","","")
* Tutorial.10 タイルモード(1) [#z295a3bc]
タイルモードはmode0~2に該当します。モード0の動作画面から見てみましょう。なお結構難しいと思うので何日か分けて取り組んでみてください。~

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

#ref(1.png,nolink)

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

#ref(2.png,nolink)

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

** パレットデータ [#hed305b0]
 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がターゲットとなります。~

** キャラクタデータ [#i7314cbd]
|  色  | ドット単位の格納方法                          | サイズ |
|  16  | 横2ドット=1byte(下位4bitが右、上位4bitが左) | 32byte |
| 256  | 横1ドット=1byte                               | 64byte |

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

|  6 |  7 |
| 14 | 15 |

*** マップデータ [#efadb02e]
キャラクタデータの並び方を決めたものがマップデータです。~
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による加工 [#x75c9144]
さて、今回は慎重に「モード0 256色タイル」のオプションと出力結果を見てみましょう。~

- bg0.grit
 -gt
 -fts
 -gu16

- bg0.h(抜粋)
 #ifndef GRIT_BG0_H
 #define GRIT_BG0_H
 
 #define bg0TilesLen 1024
 extern const unsigned short bg0Tiles[512];
 
 #define bg0PalLen 512
 extern const unsigned short bg0Pal[256];
 
 #endif // GRIT_BG0_H

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

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

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

- 対応表
| アドレス  | マップ | キャラクタ |
| 0x6000000 | 0      | 0          |
| 0x6000800 | 1      | 0          |
| 0x6001000 | 2      | 0          |
| 0x6001800 | 3      | 0          |
| 0x6002000 | 4      | 0          |
| 0x6002800 | 5      | 0          |
| 0x6003000 | 6      | 0          |
| 0x6003800 | 7      | 0          |
| 0x6004000 | 8      | 1          |
| 0x6004800 | 9      | 1          |
| 0x6005000 | 10     | 1          |
| 0x6005800 | 11     | 1          |
| 0x6006000 | 12     | 1          |
| 0x6006800 | 13     | 1          |
| 0x6007000 | 14     | 1          |
| 0x6007800 | 15     | 1          |
| 0x6008000 | 16     | 2          |
| 0x6008800 | 17     | 2          |
| 0x6009000 | 18     | 2          |
| 0x6009800 | 19     | 2          |
| 0x600A000 | 20     | 2          |
| 0x600A800 | 21     | 2          |
| 0x600B000 | 22     | 2          |
| 0x600B800 | 23     | 2          |
| 0x600C000 | 24     | 3          |
| 0x600C800 | 25     | 3          |
| 0x600D000 | 26     | 3          |
| 0x600D800 | 27     | 3          |
| 0x600E000 | 28     | 3          |
| 0x600E800 | 29     | 3          |
| 0x600F000 | 30     | 3          |
| 0x600F800 | 31     | 3          |

マップは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 [#i7bc3c1b]
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のみを使用する [#i4325621]
覚えることが大量に出てきたところでいったんクールダウンしましょう。~
とりあえず先ほどの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 [#m5a9f052]
#ref(clip_1.png,nolink)
#ref(clip_3.png,nolink)


** 履歴 [#d53b7a46]
- 2014/12/27

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