先ほどのサンプルコードを変更して移動処理を加えてみます。
int main(void) { SetMode(MODE_3 | BG2_ENABLE); s32 x=0, y=0; for(;;) { if(y > 0 && x > 0) { Mode3PutPixel(x-1, y-1, RGB5(0, 0, 0)); } Mode3PutPixel(x, y, RGB5(31, 31, 31)); x++; y++; if(y > 160) { x = 0; y = 0; } } }
変数xとyの座標には白色ドット、1つ前に描画したドットには黒色(背景色)で上書きしています。x, y共にインクリメントしているので、どのように表示されていくかは想像できると思います。ただし、残念ながらこの書き方では想像どおりの結果になりません。失敗した状態をぜひ自分の目で確かめてみてください。githubのtest_nowait.gbaを参照のこと。
失敗からわかることは、ドットが目にも留まらぬ速さで移動していることです。点が滑らかに表示されておらずブツ切りにされています。結論から言ってしまうと、垂直同期待ちの処理をしていないからです。GBAは高速で画面を書き換えています。具体的には左上0,0座標から右へ順番にドット単位で行い、その行が終わったら次の行へ、という具合です。
もう少し詳しく言い直しましょう。液晶画面は240x160ドットです。最初のラインは240ドット描画後、描画しない68ドット分が存在します。この非描画期間をH-Blankといいます。このラインが完了したら次のラインへ、0,1ドット目からとなります。160ライン分終わった後にも68ライン分存在します。この非描画期間をV-Blankといいます。
The drawing time for each dot is 4 CPU cycles. Visible 240 dots, 57.221 us, 960 cycles - 78% of h-time H-Blanking 68 dots, 16.212 us, 272 cycles - 22% of h-time Total 308 dots, 73.433 us, 1232 cycles - ca. 13.620 kHz
Visible (*) 160 lines, 11.749 ms, 197120 cycles - 70% of v-time V-Blanking 68 lines, 4.994 ms, 83776 cycles - 30% of v-time Total 228 lines, 16.743 ms, 280896 cycles - ca. 59.737 Hz
描画処理を行う場合、基本的にはV-Blankの間に行います。先ほどのサンプルコードはV-Blankを無視して描画した為、どこに描画されているかわからず、チラついて見えたりしました。V-Blankを調べるには以下のレジスタを参照します。
4000006h - VCOUNT - Vertical Counter (Read only) Indicates the currently drawn scanline, values in range from 160..227 indicate 'hidden' scanlines within VBlank area. Bit Expl. 0-7 Current Scanline (LY) (0..227) (R) 8 Not used (0) / NDS: MSB of Current Scanline (LY.Bit8) (0..262) (R) 9-15 Not Used (0)
void WaitForVsync(void) { while(*(vu16*)0x4000006 >= 160) {}; while(*(vu16*)0x4000006 < 160) {}; }
WaitForVsync関数は、VCOUNT(描画中のライン)が160になるまで待つことを意味しています。forループ内に加えると結果が良好になることがわかると思います。
#include "lib/gba.h" //--------------------------------------------------------------------------- void WaitForVsync(void) { while(*(vu16*)0x4000006 >= 160) {}; while(*(vu16*)0x4000006 < 160) {}; } //--------------------------------------------------------------------------- void Mode3PutPixel(u32 x, u32 y, u16 color) { u16* ScreenBuffer = (u16*)0x6000000; ScreenBuffer[y*240+x] = color; } //--------------------------------------------------------------------------- int main(void) { SetMode(MODE_3 | BG2_ENABLE); s32 x=0, y=0; for(;;) { WaitForVsync(); if(y != 0 || x != 0) { Mode3PutPixel(x-1, y-1, RGB5(0, 0, 0)); } Mode3PutPixel(x++, y++, RGB5(31, 31, 31)); if(y > 160) { x = 0; y = 0; } } }