Tutorial.13 割り込み

割り込みとは、その名のとおり処理が実行されているときに、
特定の条件で実行を横取りするというものです。

割り込み処理ルーチンが実行される間、
元の処理は一時的に停止されることになります。

割り込みに関して重要なI/Oレジスタは
REG_IME、REG_IE、REG_IF、REG_DISPSTATの4つです。

他にも割り込み処理関数のアドレスを登録するINT_VECTORがあります。

REG_IME

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を指定するだけです。

設定が済んだ後、1にして割り込みを発生させます。

REG_IE

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でしょうか。
V-Blankについてはチュートリアル後半に説明します。

REG_IF

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のビット内容は同じです。

REG_DISPSTAT

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

INT_VECTORには、割り込み用関数を登録します。
この関数は必ずARMコードで書きましょう。

詳しくはDoc.15 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は割り込み用の関数(ハンドラ)となっています。

フレーム数の表示例

#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);
	}
}

とりあえずカウントアップしていることだけ確認できれば大丈夫です。
サクっと次に行きましょう。

動作画面

#ref(): File not found: "clip_1.png" at page "tutorial.13"

履歴


トップ   一覧 検索 最終更新   ヘルプ   最終更新のRSS