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

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

#br

#ref(1.png,nolink)

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

#ref(2.png,nolink)

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

** パレットデータ [#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 |

#br
#ref(4.png,nolink)

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

- LCD VRAM BG Screen Data Format (BG Map)
 The display background consists of 8x8 dot tiles,
 the arrangement of these tiles is specified by the BG Screen Data (BG Map).
 The separate entries in this map are as follows:
 
 Text BG Screen (2 bytes per entry)
 Specifies the tile number and attributes.
 Note that BG tile numbers are always specified in steps of 1
 (unlike OBJ tile numbers which are using steps of two in 256 color/1 palette mode).
 
    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と書かれている0-9Bit目にキャラクタ番号を使います。マップデータは32個×32個×2バイトで2048バイトとなります。256x256ドットの大きさですが、GBAの画面サイズは240x160なので一部表示は見えていない状態です。

 #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]
今回は慎重にgritのモード0 256色タイルオプションで出力結果を見てみましょう。キャラクタデータは256色のため64*16*1 = 1024バイト。パレットデータも同様に256色のため、256*2 = 512バイトとなっています。

- 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

** 格納領域を決める [#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);

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

** REG_BGxCNT [#i7bc3c1b]

- LCD I/O BG Control 
 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つ分が必要になります。個人的にはスクロール処理が必要ない規模ならBGの大きさ指定は有功でしょう。RPGを作るのならスクロール処理は必須なので256x256一択になるのではないかと思います。

** モード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 };
 	vs32 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)
 {
 	vu32 i;
 
 	for(i=0; i<size; i++)
 	{
 		Bg[0].tileBaseAdr[i] = pDat[i];
 	}
 }
 //---------------------------------------------------------------------------
 void Bg0SetPal(u16* pDat)
 {
 	vu32 i;
 
 	for(i=0; i<256; i++)
 	{
 		BG_PALETTE[i]  = pDat[i];
 	}
 }
 //---------------------------------------------------------------------------
 void Bg0SetMap(u16* pDat, u32 size)
 {
 	vu32 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分の始点をどこにするか決めるものです。ぜひエミュレータで実行してみてください。

*** 動作画面とデバッグ [#m5a9f052]
#ref(1.png,nolink)
#ref(3.png,nolink)

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

トップ   差分 履歴 リロード   一覧 検索 最終更新   ヘルプ   最終更新のRSS