割り込みとはその名のとおり、特定の条件で実行を横取りするというものです。割り込み処理(関数)が実行される間、元の処理は一時的に停止されます。重要なレジスタはREG_IME、REG_IE、REG_IF、REG_DISPSTATです。また、割り込み処理関数のアドレスを登録するINT_VECTORがあります。
4000208h - IME - Interrupt Master Enable Register (R/W) Bit Expl. 0 Disable all interrupts (0=Disable All, 1=See IE register) 1-31 Not used
割り込み機能を使用するか使用しないかを設定します。0か1を指定するだけです。
4000200h - IE - Interrupt Enable Register (R/W) Bit Expl. 0 LCD V-Blank (0=Disable) 1 LCD H-Blank (etc.) 2 LCD V-Counter Match (etc.) 3 Timer 0 Overflow (etc.) 4 Timer 1 Overflow (etc.) 5 Timer 2 Overflow (etc.) 6 Timer 3 Overflow (etc.) 7 Serial Communication (etc.) 8 DMA 0 (etc.) 9 DMA 1 (etc.) 10 DMA 2 (etc.) 11 DMA 3 (etc.) 12 Keypad (etc.) 13 Game Pak (external IRQ source) (etc.) 14-15 Not used
どのような割り込みで、割り込み処理(関数)を呼び出すかの設定です。複数選択可能。よく使われるのはV-Blankでしょうか。
4000202h - IF - Interrupt Request Flags / IRQ Acknowledge (R/W, see below) Bit Expl. 0 LCD V-Blank (1=Request Interrupt) 1 LCD H-Blank (etc.) 2 LCD V-Counter Match (etc.) 3 Timer 0 Overflow (etc.) 4 Timer 1 Overflow (etc.) 5 Timer 2 Overflow (etc.) 6 Timer 3 Overflow (etc.) 7 Serial Communication (etc.) 8 DMA 0 (etc.) 9 DMA 1 (etc.) 10 DMA 2 (etc.) 11 DMA 3 (etc.) 12 Keypad (etc.) 13 Game Pak (external IRQ source) (etc.) 14-15 Not used
割り込みが発生したときに、現在どの条件で割り込みが起こったのか判定するためのものです。なおREG_IEとREG_IFのビット内容は同じです。
4000004h - DISPSTAT - General LCD Status (Read/Write) Display status and Interrupt control. The H-Blank conditions are generated once per scanline, including for the 'hidden' scanlines during V-Blank. Bit Expl. 0 V-Blank flag (Read only) (1=VBlank) (set in line 160..226; not 227) 1 H-Blank flag (Read only) (1=HBlank) (toggled in all lines, 0..227) 2 V-Counter flag (Read only) (1=Match) (set in selected line) 3 V-Blank IRQ Enable (1=Enable) 4 H-Blank IRQ Enable (1=Enable) 5 V-Counter IRQ Enable (1=Enable) 6-7 Not used 8-15 V-Count Setting (LYC) (0..227)
ディスプレイ関連の割り込みにはREG_IE以外にも、REG_DISPSTATに登録が必要です。片方だけしてもう一方はしなかったことのないようにお願いします。
INT_VECTORには割り込み処理(関数)のアドレスを登録します。この関数は必ずARMコードで書きましょう。詳しくはDoc.8 GBAの仕様(CPU)を参照してください。
void IrqHandler(void) { REG_IME = 0; u16 flag = REG_IF; if(flag & IRQ_xxx) { //TODO 処理内容 } REG_IF = flag; REG_IME = 1; } void IrqInit(void) { REG_IME = 0; INT_VECTOR = (u32)IrqHandler; REG_DISPSTAT = // TODO REG_IE = // TODO REG_IME = 1; }
IrqInitは初期設定用の関数。IrqHandlerは割り込み用の関数となっています。REG_IFの内容を変数にコピーして、関数の終わりにまた代入しているのはハードウェア的なもののためです。
#include "lib/gba.h" #include "res.h" volatile s32 frame; //--------------------------------------------------------------------------- void WaitForVsync(void) { while(*(vu16*)0x4000006 >= 160) {}; while(*(vu16*)0x4000006 < 160) {}; } //--------------------------------------------------------------------------- void SpriteSetPalNo(u32 num, u32 palNo) { OBJATTR* sp = (OBJATTR*)OAM + num; sp->attr2 &= 0x0fff; sp->attr2 |= (palNo << 12); } //--------------------------------------------------------------------------- void SpriteMove(u32 num, s32 x, s32 y) { OBJATTR* sp = (OBJATTR*)OAM + num; sp->attr1 &= 0xfe00; sp->attr0 &= 0xff00; sp->attr1 |= (x & 0x01ff); sp->attr0 |= (y & 0x00ff); } //--------------------------------------------------------------------------- void SpriteSetSize(u32 num, u32 size, u32 form, u32 col) { OBJATTR* sp = (OBJATTR*)OAM + num; sp->attr0 &= 0x1fff; sp->attr1 &= 0x3fff; sp->attr0 |= col | form | (160); sp->attr1 |= size | (240); } //--------------------------------------------------------------------------- void SpriteSetChr(u32 num, u32 ch) { OBJATTR* sp = (OBJATTR*)OAM + num; sp->attr2 &= 0xfc00; sp->attr2 |= ch; } //--------------------------------------------------------------------------- void SpriteInit(void) { u32 i; for(i=0; i<128; i++) { SpriteMove(i, 240, 160); } } //--------------------------------------------------------------------------- void SpriteShowNumber(u32 base, s32 num) { s32 i; for(i=5-1; i>=0; i--) { SpriteSetChr(base + i, num % 10); num /= 10; } } //--------------------------------------------------------------------------- void SpriteSetDatChr(u16* dat, u32 size) { u16* p = OBJ_BASE_ADR; u32 i; for(i=0; i<size/2; i++) { p[i] = dat[i]; } } //--------------------------------------------------------------------------- void SpriteSetDatPal(u16* pal) { u16* p = OBJ_COLORS; u32 i; for(i=0; i<16; i++) { p[i] = pal[i]; } } //--------------------------------------------------------------------------- void IrqHandler(void) { REG_IME = 0; u16 flag = REG_IF; if(flag & IRQ_VBLANK) { frame++; } REG_IF = flag; REG_IME = 1; } //--------------------------------------------------------------------------- void IrqInit(void) { REG_IME = 0; INT_VECTOR = (u32)IrqHandler; REG_DISPSTAT = LCDC_VBL; REG_IE = IRQ_VBLANK; REG_IME = 1; } //--------------------------------------------------------------------------- int main(void) { SetMode(MODE_0 | OBJ_ENABLE | OBJ_1D_MAP); SpriteInit(); SpriteSetDatChr((u16*)&sprTiles, sprTilesLen); SpriteSetDatPal((u16*)&sprPal); u32 i; for(i=0; i<5; i++) { SpriteSetSize (i, OBJ_SIZE(Sprite_8x8), OBJ_SQUARE, OBJ_16_COLOR); SpriteSetChr (i, 0); SpriteMove (i, 20+i*8, 20); SpriteSetPalNo(i, 0); } frame = 0; IrqInit(); for(;;) { WaitForVsync(); SpriteShowNumber(0, frame); } }
1フレームごとにカウントアップしていきます。割り込みの用途としてはサウンド回りで発揮されることが多いように思います。決められたタイミングで決められた音を出し、少しも遅延してはいけないという意味で必要な機能です。V-Blank中に行う処理と分けて考える必要があります。