DMA(Direct Memory Access)はCPUを介さず、高速にメモリ間のデータ転送を行うハードウェア機能です。GBAのDMAでは0~3の4つのチャンネルがあり、それぞれ特徴が異なります。違いはほとんどありませんけれど、詳しくはGBATEKを参照してください。
最も優先度が高く、転送する時間が厳密である必要があるときに使います。内部メモリから内部メモリへの転送のみ可能です。HBlank DMAにオススメです。
DMA0の次に優先度が高く、サウンド再生時のFIFOにデータを転送する時に使われることが多いです。内部メモリ・外部メモリから内部メモリへの転送が可能です。
最も優先度が低いDMA。転送先・転送元ともに内部メモリ・外部メモリのいずれでも指定できます。最も汎用的に使えるのが特徴です。
アドレス、設定について見ていきましょう。
#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)
REG_DMxSAD | Source Addressの略で、転送元のアドレスを指定します。 |
REG_DMxDAD | Destination Addressの略で、転送先のアドレスを指定します。 |
REG_DMxCNT(下位) | 転送するサイズ(16 or 32bit)を指定します。 |
REG_DMxCNT(上位) | DMA転送の設定をします。 |
転送元、転送先のアドレスの最後の桁に注意してください。正しいアドレス値でないと失敗します。エミュレータで成功する場合もありますが実機でハマるので注意してください。DMA転送に関わるすべての変数についてはALIGNマクロをつけておく事をおすすめします。
#define ALIGN(m) __attribute__((aligned (m))) u16 dataBuf[64] ALIGN(4); u16 buf[SCREEN_CX][SCREEN_CY] ALIGN(4);
転送したいバイト数を登録します。ただし転送サイズが32ビットの場合は4(バイト)で、16ビットの場合は2であらかじめ割っておく必要があります。
#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からの要求があった時に転送を開始します。こちらも詳しくは不明です。
#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(); } }