- 追加された行はこの色です。
- 削除された行はこの色です。
#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