ボタン入力はアドレス0x4000130をu16単位で参照します。下位のビットから順番に、Aボタン、Bボタン、セレクトボタン、スタートボタン、十字キーの→、←、↑、↓、最後はRボタン、Lボタンとなっています。ビットが立っている時は押されていない状態、ビットが立っていない時は押されている状態です。どの程度の強さで押されているか、といった判定はできません。
ビット | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
ボタン | L | R | ↓ | ↑ | ← | → | ST | SL | B | A |
The built-in GBA gamepad has 4 direction keys, and 6 buttons. 4000130h - KEYINPUT - Key Status (R) Bit Expl. 0 Button A (0=Pressed, 1=Released) 1 Button B (etc.) 2 Select (etc.) 3 Start (etc.) 4 Right (etc.) 5 Left (etc.) 6 Up (etc.) 7 Down (etc.) 8 Button R (etc.) 9 Button L (etc.) 10-15 Not used It'd be usually recommended to read-out this register only once per frame, and to store the current state in memory. As a side effect, this method avoids problems caused by switch bounce when a key is newly released or pressed.
キー入力を取得するにはメインループ中で毎回状態をチェックするか、割り込み機能を使って取得します。今回は簡単な前者の方法を使ってみましょう。以下のenumを用意します。
#define REG_KEYINPUT *(vu16*)(REG_BASE + 0x130) // Key Input typedef enum KEYPAD_BITS { KEY_A = (1<<0), /*!< keypad A button */ KEY_B = (1<<1), /*!< keypad B button */ KEY_SELECT = (1<<2), /*!< keypad SELECT button */ KEY_START = (1<<3), /*!< keypad START button */ KEY_RIGHT = (1<<4), /*!< dpad RIGHT */ KEY_LEFT = (1<<5), /*!< dpad LEFT */ KEY_UP = (1<<6), /*!< dpad UP */ KEY_DOWN = (1<<7), /*!< dpad DOWN */ KEY_R = (1<<8), /*!< Right shoulder button */ KEY_L = (1<<9), /*!< Left shoulder button */ KEYIRQ_ENABLE = (1<<14), /*!< Enable keypad interrupt */ KEYIRQ_OR = (0<<15), /*!< interrupt logical OR mode */ KEYIRQ_AND = (1<<15), /*!< interrupt logical AND mode */ DPAD = (KEY_UP | KEY_DOWN | KEY_LEFT | KEY_RIGHT) /*!< mask all dpad buttons */ } KEYPAD_BITS;
ところで入力の説明が簡単すぎたので別の話をさせてもらいます。今まで何気なくビットマップモードから説明をしてきました。REG_DISPCNTなどのレジスタに設定したり、VRAMに色データを設定したり、そして今回のREG_KEYINPUTは値を読み取ってボタンの状態を確認しています。もう少し概念的にいうと、決められた設定データをアドレスに配置する、または決められたアドレスの値を調べる、という考え方に集約されます。設定を忘れたり、データを配置し忘れると当然うまくいきません。ここではプログラマーの意図した結果になっているのか?という確認方法をまとめてご紹介します。
エミュレータのMemory viewerです。Toolsからぶら下がっているものはすべて有用なので一度確認しておくことをおすすめします。
gritのオプションを間違うことはよくあることです。自分の意図したサイズかどうか、あと1、2バイト目のデータ構造ぐらいは確認した方がいいかもしれません。
私のサンプルコード限定ですけれど、使い方は以下のとおりです。
u32 x = 1234; TRACE("%d %x\n", x, x);
出力結果はエミュレータのTools->Loggingで確認できます。この関数は自作したものなので標準ライブラリと関係ありません。原理についてはDoc.10 エミュレータのデバッグコンソールを読んでください。ところで、そろそろ標準ライブラリは使えないのかという疑問も出てくると思います。
#include <stdio.h>
というように山括弧を使えばできますが、入出力関係の関数は全滅です。なぜならfopen、fgetcはファイルシステムがないので使えず、scanfはキーボード入力が物理的にないので使えず、printfはそもそも日本語フォントが内蔵されておらず、表示手段もないので使えず。。OSがないってなんて不便なんや、と文句いいたくなりますね。有用なのは数学関係の関数でしょうか。
#include <math.h>
ただ標準ライブラリは全部カードリッジ領域固定なのでスピードが出ません。あとROMのサイズも増大するのであまりおすすめしません。
ボタン入力のサンプルコードです。
#include "lib/gba.h" //--------------------------------------------------------------------------- void WaitForVsync(void) { while(*(vu16*)0x4000006 >= 160) {}; while(*(vu16*)0x4000006 < 160) {}; } //--------------------------------------------------------------------------- int main(void) { SetMode(MODE_3 | BG2_ENABLE); u32 x = 0; for(;;) { WaitForVsync(); if( !(REG_KEYINPUT & KEY_UP) ) TRACE("上 ボタン\n"); if( !(REG_KEYINPUT & KEY_DOWN) ) TRACE("下 ボタン\n"); if( !(REG_KEYINPUT & KEY_RIGHT)) TRACE("右 ボタン\n"); if( !(REG_KEYINPUT & KEY_LEFT) ) TRACE("左 ボタン\n"); if( !(REG_KEYINPUT & KEY_A) ) TRACE("10進=%d 16進=0x%x 16進4桁=0x%04x \n", x, x, x); if( !(REG_KEYINPUT & KEY_B) ) TRACE("変数x++ \n", x++); } }
ちなみにボタン入力はパソコンの物理キーボードの性能に依存しています。たとえば上下右左の4つのボタンを同時にした結果、正しいデバック表示になるかというとかなり怪しいです。個人的には安いキーボードほど同時ボタン処理が適当なイメージを持っています。実機の場合とでは動作が違うということを覚えておいてください。