ウィンドウとDMA(HBLANK)機能を使ったサンプルコードです。メトロイドの序盤で表示されるライトをほぼ再現しています。
構成は以下の通りです。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)®_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_DMA3CNT = 0; REG_DMA3SAD = (u32)&BgWinh[1]; REG_DMA3DAD = (u32)®_WIN0H; REG_DMA3CNT = 1 | (DMA_DST_RELOAD | DMA_REPEAT | DMA_HBLANK | DMA_ENABLE);