先ほどのサンプルコードを変更して移動処理を加えてみます。
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共にインクリメントしているので、どのように表示されていくかは想像できると思います。ただし、残念ながらこの書き方では想像どおりの結果になりません。失敗した状態をぜひ自分の目で確かめてください。ダウンロードはこちらです。
失敗からわかることは、点が滑らかに表示されておらずブツ切りにされています。結論から言ってしまうと垂直同期待ちの処理をしていないからです。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になるまで待つことを意味しています。先ほどのコードに加えた違いを確認してください。結果が良好になることがわかると思います。
#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; } } }