DMA(Direct Memory Access)はCPUを介さず、高速にメモリ間のデータ転送を行うハードウェア機能です。GBAのDMAでは0~3の4つのチャンネルがあり、それぞれ特徴が異なります。違いはほとんどありませんけれど、詳しくはGBATEKを参照してください。
アドレス、設定についてを見ていきましょう。
#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転送の設定をします。 |
アライメントの関係で転送元、転送先のアドレスの最後の桁に注意してください。
でないと失敗します。エミュレータで成功する場合もありますが実機でハマります。
#define ALIGN(m) __attribute__((aligned (m))) u16 dataBuf[64] ALIGN(4); u16 buf[SCREEN_CX][SCREEN_CY] ALIGN(4);
DMA転送に関わるすべての変数については、ALIGNマクロをつけておく事をおすすめします。
転送したいバイト数を登録します。ただし転送サイズが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はアドレスを固定し、同じアドレスのデータを転送するようになります。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設定後の、転送開始のタイミングです。
#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(); } }