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

GBAの画面の表示方法には大きく分けて2種類あります。それはビットマップモードとタイルモードです。さらにビットマップモードではMode0, 1, 2とあってビットマップモードではMode3, 4, 5と別れています。図にすると次のようなものです。

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

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

ビットマップモード

このモードはとあるメモリ領域に値を書き込むと、それがそのまま画面に反映されます。C言語ではint変数に123を代入するとき「int abc = 123;」という書き方をしますが、この123が色データの役割を持ち、abcはメモリ領域(そのまま画面に反映されるもの)だ、と思ってください。GBAの画面サイズは240x160ドットです。1ドットにつき2バイトで表現します。240x160x2バイトなので容量は76800バイト。メモリ領域(VRAM)は、0x6000000番地から開始されます。

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

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

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

引数xの幅は0~240、引数yの幅は0~160。それ以上の値を放り込むと簡単に壊れてしまう関数ではあります。最後の引数colorは、色(RGB)データでマクロを使って表します。

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

下位ビットから順番に赤、緑、青がそれぞれ5bitづつ、0~31の32段階を指定して使います。いきなりビットシフトは難しいかもしれません。そこで白色を代入した場合のプログラム的な動きを見ていきましょう。

u16 color = RGB5(31,31,31);

マクロですので次のように分解されます。

u16 color = ((31)|((31)<<5)|((31)<<10));

31とは

#ref(): File not found: "clip_1.gif" at page "tutorial.2"

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

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

もう少し詳しく言い直しましょう。液晶画面は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ドット表示

#ref(): File not found: "clip_2.png" at page "tutorial.2"

履歴


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