Tutorial.2 ビットマップモード(1)

画面モード

GBAの画面の表示方法には大きく分けて2種類あり、ビットマップモードと
タイルモードがあります。さらに細かい区分けをすると以下のようになっています。

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

まず最初はビットマップモードの3〜5を練習してみましょう。

ビットマップモード

このモードはとあるメモリ領域に値を書き込むと、それがそのまま画面に反映されます。
擬似的なソースコードを簡単に書くと

メモリ領域 = 色;

とすると、画面にドットの色が表示されるわけです。

GBAの画面サイズは240x160ドットですが、ここでは1ドットにつき2バイトで表現します。
240x160x2バイトなので容量は76800バイト。メモリ領域(VRAM)は、0x6000000番地から開始されます。

領域名称開始位置終了位置大きさ(バイト)
VRAM(Video RAM)0x60000000x6012C0076800

ドットを表示する関数をソースコードで書くと以下になります。

void Mode3PutPixel(u32 x, u32 y, u16 col)
{
	u16* ScreenBuffer     = (u16*)0x6000000;
	ScreenBuffer[y*240+x] = col;
}

この引数colは、色(RGB)データです。マクロを使って表します。

#define RGB5(r,g,b)((r)|((g)<<5)|((b)<<10))
rgb
313131
3100
0310
0031
000

下位ビットから順番に赤、緑、青がそれぞれ5bitづつ、0〜31の32段階を指定して使います。

clip_1.gif

モード3でドットを描く

とりあえず1ドットだけ出してみましょう。GBA版Hallo Worldです。

#include "lib/gba.h"

//---------------------------------------------------------------------------
void WaitForVsync(void)
{
	while(*(vu16*)0x4000006 >= 160) {};
	while(*(vu16*)0x4000006 <  160) {};
}
//---------------------------------------------------------------------------
void Mode3PutPixel(u32 x, u32 y, u16 col)
{
	u16* ScreenBuffer     = (u16*)0x6000000;
	ScreenBuffer[y*240+x] = col;
}
//---------------------------------------------------------------------------
int main(void)
{
	// モード設定
	SetMode(MODE_3 | BG2_ENABLE);

	// ドットの描画
	u32 x   = 50;
	u32 y   = 50;
	u16 col = RGB5(31,31,31);

	Mode3PutPixel(x, y, col);


	for(;;)
	{
	    WaitForVsync();
	}

}

まず始めに、グラフィックスのモードを設定します。

SetMode(MODE_3 | BG2_ENABLE);

グラフィックスモードの設定はヘッダファイルにあるSetMode()マクロを使っています。
この内容を分解してみると

// gba.h

static inline void SetMode(int mode)	{REG_DISPCNT = mode;}

#define	REG_DISPCNT	*((vu16 *)(REG_BASE + 0x00))
#define	REG_BASE	0x04000000
*((vu16*)(0x04000000 + 0x00)) = MODE_3 | BG2_ENABLE

と同じ意味になり、0x04000000〜0x04000001に値を入れることで
モードの設定をしています。

ビットマップモードで使用するBG番号は2のみですが、
詳しいことはタイルモードのところで説明します。

u16* ScreenBuffer     = (u16*)0x6000000;

次に、ScreenBufferポインタにVRAMを指定させて、
操作をしやすいように2バイト(16bit)=1ドット単位としてu16宣言をしています。

void Mode3PutPixel(u32 x, u32 y, u16 col)
{
	u16* ScreenBuffer     = (u16*)0x6000000;
	ScreenBuffer[y*240+x] = col;
}

最後は垂直同期待ちの処理です。

void WaitForVsync(void)
{
	while(*(vu16*)0x4000006 >= 160) {};
	while(*(vu16*)0x4000006 <  160) {};
}

GBAは高速で画面を書き換えています。
具体的には左上から右へ順番にドット単位で行い、
そのラインが終わったら次のラインへ、という具合です。

clip_3.png

もう少し詳しく言い直しましょう。液晶画面は240x160ドットです。
最初のラインは240ドット描画後、描画しない68ドット分が存在します。
この非描画期間をH-Blankといいます。

Horizontal Dimensions
The drawing time for each dot is 4 CPU cycles.

 Visible     240 dots,  57.221 us,    960 cycles - 78% of h-time
 H-Blanking   68 dots,  16.212 us,    272 cycles - 22% of h-time
 Total       308 dots,  73.433 us,   1232 cycles - ca. 13.620 kHz

仕様書を読むと1ドット描画するのにCPUで4サイクル。
240+68=308ドットで1232サイクルと書いてありますね。

次に、160ライン分終わった後にも68ライン分存在します。
この非描画期間をV-Blankといいます。

Vertical Dimensions

 Visible (*) 160 lines, 11.749 ms, 197120 cycles - 70% of v-time
 V-Blanking   68 lines,  4.994 ms,  83776 cycles - 30% of v-time
 Total       228 lines, 16.743 ms, 280896 cycles - ca. 59.737 Hz

描画処理を行う場合、基本的にはこのV-Blankの間に行います。
V-Blankを無視して描画した場合、チラついて見えたりするのでオススメしません。
サンプルでは無視していますけどね。(^^;

先ほどの関数は、VCOUNT(描画中のライン)が160になるまで待つことを
意味しています。

動作画面

x座標:50、y座標:50に、白色を1ドット表示

clip_2.png

履歴

  • 2014/12/21

添付ファイル: fileclip_3.png 239件 [詳細] fileclip_2.png 245件 [詳細] fileclip_1.gif 258件 [詳細]

Last-modified: 2015-01-04 (日) 23:25:44 (2689d)