DMA

DMA(Direct Memory Access)はCPUを介さず、高速にメモリ間のデータ転送を行うハードウェア機能です。GBAのDMAでは0~3の4つのチャンネルがあり、それぞれ特徴が異なります。違いはほとんどありませんけれど、詳しくはGBATEKを参照してください。

DMA0

最も優先度が高く、転送する時間が厳密である必要があるときに使います。内部メモリから内部メモリへの転送のみ可能です。HBlank DMAにオススメです。

DMA1とDMA2

DMA0の次に優先度が高く、サウンド再生時のFIFOにデータを転送する時に使われることが多いです。内部メモリ・外部メモリから内部メモリへの転送が可能です。

DMA3

最も優先度が低いDMA。転送先・転送元ともに内部メモリ・外部メモリのいずれでも指定できます。最も汎用的に使えるのが特徴です。

I/Oアドレス

#define REG_DMA0SAD	*(vu32*)(REG_BASE + 0x0b0)
#define REG_DMA0DAD	*(vu32*)(REG_BASE + 0x0b4)
#define REG_DMA0CNT	*(vu32*)(REG_BASE + 0x0b8)

#define REG_DMA1SAD	*(vu32*)(REG_BASE + 0x0bc)
#define REG_DMA1DAD	*(vu32*)(REG_BASE + 0x0c0)
#define REG_DMA1CNT	*(vu32*)(REG_BASE + 0x0c4)

#define REG_DMA2SAD	*(vu32*)(REG_BASE + 0x0c8)
#define REG_DMA2DAD	*(vu32*)(REG_BASE + 0x0cc)
#define REG_DMA2CNT	*(vu32*)(REG_BASE + 0x0d0)

#define REG_DMA3SAD	*(vu32*)(REG_BASE + 0x0d4)
#define REG_DMA3DAD	*(vu32*)(REG_BASE + 0x0d8)
#define REG_DMA3CNT	*(vu32*)(REG_BASE + 0x0dc)

それぞれ各3づつI/Oアドレスを持ちます。

REG_DMxSADSource Addressの略で、転送元のアドレスを指定します。
REG_DMxDADDestination Addressの略で、転送先のアドレスを指定します。
REG_DMxCNT(下位)転送するサイズ(16 or 32bit)を指定します。
REG_DMxCNT(上位)DMA転送の設定をします。

REG_DMxSAD、REG_DMxDAD

転送元、転送先のアドレスの最後の桁に注意してください。正しいアドレス値でないと失敗します。エミュレータで成功する場合もありますが実機でハマるので注意してください。DMA転送に関わるすべての変数についてはALIGNマクロをつけておく事をおすすめします。

#define ALIGN(m)	__attribute__((aligned (m)))

u16 dataBuf[64] ALIGN(4);
u16 buf[SCREEN_CX][SCREEN_CY] ALIGN(4);

REG_DMxCNT_L

転送したいバイト数を登録します。ただし転送サイズが32ビットの場合は4(バイト)で、16ビットの場合は2であらかじめ割っておく必要があります。

REG_DMxCNT_H

#define DMA_DST_INC		(0<<21)
#define DMA_DST_DEC		(1<<21)
#define DMA_DST_FIXED		(2<<21) // 固定
#define DMA_DST_RELOAD		(3<<21)

#define DMA_SRC_INC		(0<<23)
#define DMA_SRC_DEC		(1<<23)
#define DMA_SRC_FIXED		(2<<23) // 固定

転送アドレスをどう変化させるかの設定です。FIXEDはアドレスを固定し同じアドレスのデータを転送するようになります。0クリアなどに便利ですね。DMA_DST_RELOADは転送ごとに増加しすべての転送が終わった時に最初のアドレスに戻します。

#define DMA_REPEAT		(1<<25)

一度のDMA転送が終了したあとに次回のDMA転送を繰り返し行うか行わないかの設定です。サウンド再生用FIFOへの転送や、フレームごとの転送を行う場合はONにします。

#define DMA_IRQ			(1<<30)

DMAの転送終了時に割り込みを発生させるかの設定です。

#define DMA_ENABLE		(1<<31)

DMAが有効になります。

#define DMA_IMMEDIATE		(0<<28)
#define DMA_VBLANK		(1<<28)
#define DMA_HBLANK		(2<<28)
#define DMA_SPECIAL		(3<<28)

DMA_ENABLE設定後の、転送開始のタイミングです。DMA_IMMEDIATEはDMA_ENABLEが設定されるとすぐに転送を開始します。VBLANKは1秒間に60回行われる画面の更新時に転送を開始します。HBLANKは画面の更新時の横1列の描画時に転送を開始します。SPECIALは、DMA1CNT_Hのみサウンド機能のFIFO要求があった時にDMA転送を開始します。またSPECIALは、DMA3CNT_Hのみビットマップモードで次のフレームの内容を毎回転送したい場合に使います。詳しくは不明です。

#define GAMEPAK_DRQ		(1<<27)

ゲームパックROMからの要求があった時に転送を開始します。こちらも詳しくは不明です。

Mode3で画像をDMA転送例

#include "lib/gba.h"
#include "res.h"
 
//---------------------------------------------------------------------------
void WaitForVsync(void)
{
	while(*(vu16*)0x4000006 >= 160) {};
	while(*(vu16*)0x4000006 <  160) {};
}
//---------------------------------------------------------------------------
int main(void)
{
	SetMode(MODE_3 | BG2_ENABLE);

	REG_DMA3SAD = (u32)&imageBitmap;
	REG_DMA3DAD = (u32)VRAM;
	REG_DMA3CNT = (u32)(240*160) | (DMA_SRC_INC | DMA_DST_INC | DMA16 | DMA_ENABLE);

	for(;;)
	{
	    WaitForVsync();
	}
}

動作画面

1.png

2023/07/27追記

DMAは割り込み中にのみ使用してください。Toncで警告していました。CPUを停止させて割り込みがダメになるとのこと。BIOSのCpuFaseSetか、DMA予約配列を作って割り込み中に一気に転送するなどの処置が必要です。上記のサンプル例はやってはいけない例ということで・・・。

履歴


添付ファイル: file1.png 21件 [詳細]

トップ   差分 履歴 リロード   一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2023-07-27 (木) 11:51:22