DMAの応用例

ウィンドウとDMA HBLANK機能を使ったサンプルコードです。元ネタのToncチュートリアルからdevkitPro用に変換させて頂きました。メトロイド序盤で表示されるライトをほぼ再現しています。

1.png

構成は以下の通りです。mode0を使用して16色。bg0:ASCII文字(256x256)、bg1:マスク(256x256)、bg2:ステージ(512x256)となっておりステージのみ512x256です。

	REG_BG2HOFS = 120;
	REG_BG2VOFS = 64;

	REG_WIN0H  = 0;
	REG_WIN0V  = SCREEN_HEIGHT;
	REG_WININ  = WIN_0_BG0 | WIN_0_BG2;
	REG_WINOUT = WIN_0_BG0 | WIN_0_BG1;

ステージ表示には始点座標を変えています。さらにウィンドウの内部と外部にどれを表示させるかの設定を加えています。

/* Create an array of horizontal offsets for a circular window.
*	The offsets are to be copied to REG_WINxH each HBlank, either 
*	by HDMA or HBlank isr. Offsets provided by modified 
*	Bresenham's circle routine (of course); the clipping code is not
*	optional.
*
*	\param x0	X-coord of circle center.
*	\param y0	Y-coord of circle center.
*	\param rr	Circle radius.
*/

u16 BgWinh[SCREEN_HEIGHT+1] ALIGN(4);

IWRAM_CODE void BgCreateWindowCircleDma(s32 x0, s32 y0, s32 rr)
{
	// Zero clear
	for(vs32 i=0; i<SCREEN_HEIGHT+1; i++)
	{
		BgWinh[i] = 0;
	}


	s32 x=0, y=rr, d=1-rr;
	u32 tmp;

	while(y >= x)
	{
		// Side octs
		tmp  = BgClamp(x0 + y, 0, SCREEN_WIDTH+1);
		tmp += BgClamp(x0 - y, 0, SCREEN_WIDTH+1) << 8;

		// o4, o7
		if(BgInRange(y0-x, 0, SCREEN_HEIGHT))
		{
			BgWinh[y0 - x]= tmp;
		}

		// o0, o3
		if(BgInRange(y0+x, 0, SCREEN_HEIGHT))
		{
			BgWinh[y0 + x]= tmp;
		}

		// Change in y: top/bottom octs
		if(d >= 0)
		{
			tmp  = BgClamp(x0 + x, 0, SCREEN_WIDTH+1);
			tmp += BgClamp(x0 - x, 0, SCREEN_WIDTH+1) << 8;

			// o5, o6
			if(BgInRange(y0-y, 0, SCREEN_HEIGHT))
			{
				BgWinh[y0 - y]= tmp;
			}

			// o1, o2
			if(BgInRange(y0+y, 0, SCREEN_HEIGHT))
			{
				BgWinh[y0 + y]= tmp;
			}

			d -= 2 * (--y);
		}

		d += 2 * (x++) + 3;
	}

/*
	for(vs32 i=0; i<SCREEN_HEIGHT+1; i++)
	{
		TRACE("%d: %4x\n", i, BgWinh[i]);
	}
	for(;;){}
*/

	REG_DMA3CNT = 0;
	REG_DMA3SAD = (u32)&BgWinh[1];
	REG_DMA3DAD = (u32)&REG_WIN0H;
	REG_DMA3CNT = 1 | (DMA_DST_RELOAD | DMA_REPEAT | DMA_HBLANK | DMA_ENABLE);
}
//---------------------------------------------------------------------------
IWRAM_CODE s32 BgClamp(s32 val, s32 min, s32 max)
{
	if(val < min)
	{
		return min;
    }

	if(val > max)
	{
		return max;
	}

	return val;
}
//---------------------------------------------------------------------------
IWRAM_CODE bool BgInRange(s32 x, s32 min, s32 max)
{
	return ((x)>=(min)) && ((x)<(max)) ? TRUE : FALSE;
}

注目してほしいのは、以下のコードでHBLANK毎にDMAを発生させていることです。GBAの画面の高さは160ですので160回HBLANKを発生させます。1ライン分のウィンドウサイズを2バイト、REG_WIN0Hに格納しています。HBLANK毎にウィンドウ値を決定するわけです。チラツキもなく実現しており、非常に芸術点が高い作品となっています。ぜひ動作確認をしてみてください。

	REG_DMA3CNT = 0;
	REG_DMA3SAD = (u32)&BgWinh[1];
	REG_DMA3DAD = (u32)&REG_WIN0H;
	REG_DMA3CNT = 1 | (DMA_DST_RELOAD | DMA_REPEAT | DMA_HBLANK | DMA_ENABLE);

出典元

履歴


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