#author("2023-04-27T19:13:05+09:00","","")
#freeze
#author("2023-05-26T19:57:57+09:00","","")
* 割り込み [#rc777b53]
割り込みとはその名のとおり、特定の条件で実行を横取りするというものです。割り込み処理(関数)が実行される間、元の処理は一時的に停止されます。重要なレジスタはREG_IME、REG_IE、REG_IF、REG_DISPSTATです。また、割り込み処理関数のアドレスを登録するINT_VECTORがあります。~
割り込みとは特定の条件で実行を横取りするものです。割り込み処理が実行される間、元の処理は一時的に停止されます。イマイチよくわからないという方はソースコードを読んだ方が早いと思われます。重要なレジスタはREG_IME、REG_IE、REG_IF、REG_DISPSTATです。また、割り込み処理関数のアドレスを登録するINT_VECTORがあります。それぞれ見ていきましょう。

** REG_IME [#d059e10c]
 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を指定するだけです。~
割り込み機能を使用するか使用しないかを設定します。0か1を指定するだけです。

** REG_IE [#e2906346]
 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 [#fb52fab3]
 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_IEとREG_IFのビット内容は同じです。

** REG_DISPSTAT [#he1d3eb9]
 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に登録が必要です。片方だけしてもう一方はしなかったことのないようにお願いします。~
ディスプレイ関連の割り込みにはREG_IE以外にも、REG_DISPSTATに登録が必要です。片方だけしてもう一方はしなかった、ということのないようにお願いします。

** INT_VECTOR [#s9d02447]
INT_VECTORには割り込み処理(関数)のアドレスを登録します。この関数は必ずARMコードで書きましょう。詳しくは[[Doc.8 GBAの仕様(CPU)>doc.8]]を参照してください。~
INT_VECTOR(3007FFCh)には割り込み処理(関数)のアドレスを登録します。この関数は必ずARMコードで書きましょう。詳しくは[[Doc.8 GBAの仕様(CPU)>doc.8]]を参照してください。

** 設定方法(テンプレート) [#id41ea3a]

 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_DISPSTAT = // TODO 割り込みを指定
 	REG_IE       = // TODO 割り込みを指定
 
 	REG_IME = 1;
 }

IrqInitは初期設定用の関数。IrqHandlerは割り込み用の関数となっています。REG_IFの内容を変数にコピーして、関数の終わりにまた代入しているのはハードウェア的なもののためです。~
IrqInitは初期設定用の関数。IrqHandlerは割り込み用の関数となっています。IrqHandler関数の最後で不必要な代入をしているREG_IFは、ハードウェア的な意味らしいです。

** フレーム数の表示例 [#ib4634e3]
 #include "lib/gba.h"
 #include "res.h"
 
 volatile s32 frame;
 vs32 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中に行う処理と分けて考える必要があります。~

1フレームごとにカウントアップしています。基本的に割り込み処理は優先しなくてはいけないものに使いましょう。用途としてはV-BlankまでHALTをするか、サウンドでタイミングを合わせるために使われることが多いようです。音楽のテンポが悪くなったら台無しですからね。

*** 動作画面 [#c5a3ca8e]
#ref(1.png,nolink)

** 履歴 [#jed99b90]
- 2023/04/27
- 2023/04/29
- 2007/09/05


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