# 割り込み ## 👾 レジスタ ### IME - 全割り込み有効フラグ (W) ``` 0 - 全ての割り込みが無効 1 - IEレジスタ(0xffff)で有効になっている割り込みが全て有効 ``` IMEフラグは、すべての割り込みを無効にするために使用され、IEレジスタの有効ビットよりも優先されます。 I/Oアドレスを使ってIMEフラグにアクセスすることはできません。IMEは、以下の命令/イベントによってのみ変更可能です。 ``` EI ; 割り込みを有効化 (つまり、IME=1) DI ; 割り込みを無効化 (つまり、IME=0) RETI ; 割り込みを有効化してリターン (EI, RETを実行したのと同様) ; 割り込みを無効化して割り込みベクタを呼び出す ``` ここで\とは、CPUが割り込みを実行する際に自動的に実行される動作を意味します。 EIの効果は1命令分だけ遅れることに注意してください。つまり、EIに続いてすぐにDIを実行しても、その間の割り込みは許されません。 ### FFFF - IE - 割り込み有効フラグ (R/W) ``` Bit 0: VBlank割り込み有効フラグ (INT 40h) (1=有効) Bit 1: STAT割り込み有効フラグ (INT 48h) (1=有効) Bit 2: Timer割り込み有効フラグ (INT 50h) (1=有効) Bit 3: Serial割り込み有効フラグ (INT 58h) (1=有効) Bit 4: Joypad割り込み有効フラグ (INT 60h) (1=有効) ``` ### FF0F - IF - 割り込みフラグ (R/W) ``` Bit 0: VBlank割り込みリクエスト (INT 40h) (1=リクエストあり) Bit 1: STAT割り込みリクエスト (INT 48h) (1=リクエストあり) Bit 2: Timer割り込みリクエスト (INT 50h) (1=リクエストあり) Bit 3: Serial割り込みリクエスト (INT 58h) (1=リクエストあり) Bit 4: Joypad割り込みリクエスト (INT 60h) (1=リクエストあり) ``` bitと割り込みの種類の対応関係はIEと同じです。 割り込み信号がLowからHighに変化すると、IFレジスタの対応するbitがセットされます。例えば,LCDコントローラがVBlank期間に入ると,bit0がセットされます。 IFレジスタのbitのセットは、割り込みの実行を**要求しているだけ**です。実際に実行されるのは、IMEフラグとIEレジスタの対応するbitの両方がセットされている場合のみで、そうでない場合はIMEとIEの両方が実行を許可するまで割り込みは待機します。 IFレジスタのbitはCPUが自動的にセット/クリアするので、通常はゲームプログラムによるIFレジスタへの書き込みは必要ありません。しかし、ゲームプログラムが手動で割り込みを要求(または破棄)するために、IFレジスタへの書き込みを行うことは可能です。実際の割り込みと同様に、手動で要求された割り込みは、IMEとIEが実行を許可しない限り、実行されません。 ## 割り込みのハンドリング まず割込みに対応するIFビットとIMEフラグは、CPUによってリセットされます。前者は割り込みを認識し、後者はプログラムが`reti`命令を使用して再び割り込みを有効にするまで、それ以降の割り込みを処理できないようにするためです。 その後、対応する割込みハンドラ(上記のIEおよびIFレジスタの説明を参照)がCPUから呼び出されます。これは通常の呼び出しで、`call `命令で実行されるのとまったく同じ処理が行われます。つまり、現在のPCがスタックにプッシュされ、その後、割り込みベクタのアドレスに設定されます。 Z80のデータシートによると、割り込みハンドラに制御を移す際に以下のような現象が発生します。 1. 2マシンサイクル待機します。この間には何も変化は起きないので、この間にCPUがNOPを実行していると思われます。 2. 現在のPCがスタックにプッシュされます。この処理でさらに2マシンサイクルを消費します。 3. PCの上位バイトは0に、下位バイトはハンドラのアドレス($40,$48,$50,$58,$60)に設定されます。これにより、最後に1マシンサイクルが消費されます。 結局割り込みハンドラへの移行で消費されるマシンサイクルは、5マシンサイクルとなります。これはまだテストしていませんが、Z80のデータシートに記載されている通りです。 ## 優先度 以下の3つの状況では、IFレジスタの複数のビットが設定され、一度に複数の割り込みが要求されることがあります。 ``` - 複数の割込み信号が同時に Low から High に変化した場合。 - IME/IE が直接処理できない間に、複数の割り込みがリクエストされた場合。 - ゲームプログラムが複数のbitがセットされた値(例: 0b0001_1111)を IF に手動で書き込んだ場合。 ``` IME および IE が、要求された割り込みのうち複数の実行を許可している場合、優先順位の高い割り込みが最初に実行されます。 優先順位は、IEおよびIFレジスタのbitと同じ順番になります。つまり、bit0(VBlank)の優先順位が最も高く、bit4(Joypad)の優先順位が最も低くなります。 ## 割り込みのネスト CPUは割り込み実行時に`IME=0`にすることで、他の割り込みを自動的に無効にします。 通常、割り込みハンドラがリターンする(RETI命令で`IME=1`にする)まで`IME=0`のままです。 しかし、割り込みハンドラの内部から他の割り込み(任意の優先度)の実行を許可したい場合は、割り込みハンドラ内で`EI`命令を使用します。 ## 割り込みの種類 ### INT 40 - VBlank割り込み この割り込みは、ゲームボーイがVBlankに入るたびにリクエストされます。 VBlank割り込みは、ゲームボーイ(DMGまたはCGB)またはゲームボーイプレーヤーでは1秒間に約59.7回、スーパーゲームボーイ(SGB)では1秒間に約61.1回発生します。 この割り込みは、VBlank期間(`LY=144`)の開始時に発生します。この期間は、ビデオハードウェアがVRAMを使用していないため、自由にVRAMにアクセスすることができます。 この期間は約1.1ミリ秒です。 ### INT 48 - STAT割り込み STATレジスタに記述されているように、この割り込みを発生させるトリガーとなるソースはモード0-2、LYCなど様々です。 様々なSTAT割り込みソースは、それぞれの有効bitがオンになると、その状態が論理的にORされ、**STAT割り込みライン**になります。 ```py # bitNは LCDStatus($FF41)の各bit STAT割り込みライン = bit6 | bit5 | bit4 | bit3 ``` STAT割り込みは、このSTAT割り込みラインの立ち上がり、つまりLowからHighへの遷移でトリガーされます。 STAT割り込みソースが、他のソースによって割り込みラインがすでにHighに設定されている間(またはその直後)に、論理的にORしてHighにした場合は、LowからHighへの遷移がないため、割り込みは発生しません。この現象は`STAT blocking`と呼ばれています。 STATレジスタの説明で述べたように、PPUは異なるモードを一定の順序で循環させます。そのため、例えば、モード0とモード1のように2つの連続したモードに対して割り込みが有効になっている場合、(STAT割り込みラインがそれらの間でLowになる機会がないため)モード1に対しては割り込みは発生しません。 **STAT割り込みの使い道** よく使われる用途としては、ビデオハードウェアが特定のLCDラインを再描画しようとしているときに、ユーザーにその旨を伝えることです。 これは、SCX/SCYレジスタ(`$FF43/$FF42`)を動的に制御して、特殊なグラフィック効果を実行するのに役立ちます。 具体的な例を挙げるなら、LYCをWYに設定し、LY=LYC割り込みを有効にして、割り込みハンドラの処理でスプライトを無効にします。これは、ウィンドウをテキストボックスとして画面の下部に使用し、スプライトをテキストボックスで隠したい場合に役に立ちます。 ### INT 50 - タイマー割り込み タイマーがオーバーフローするたびに(つまり、TIMAが`$FF`を超えるたびに)、IFレジスタのbit2(`$FF0F`)をセットすることでタイマー割り込みが要求されます。その割り込みが有効になるとすぐに、CPUは`$50`のタイマー割り込みベクタを呼び出して実行します。 ### INT 58 - シリアル通信割り込み [シリアル通信のページ](./serial.md)を参照 ### INT 60 - ジョイパッド割り込み ジョイパッド割り込みは、P1のbit0-3のいずれかがHighからLowに変化したときに要求されます。 これは、(ボタン/十字キーがそれぞれbit5/4で有効になっている場合に)ボタンが押されたときに発生しますが、スイッチのバウンスのため、ボタンを押したときには通常1回以上のHighからLowへの遷移が発生してしまいます。 **ジョイパッド割り込みの使い道** この割り込みは、ボタン(bit5)または十字キー(bit4)のどちらか一方のみが選択されていて、両方が同時に選択されていない場合に、ボタンの押下を識別するのに便利です。 両方が選択されていて、例えばボタンによってbitが既にLowに保たれている場合、対応する方向ボタンを押しても判別はできません。 ジョイパッド割り込みの唯一の使い道は、STOP(低電力)状態を終了させることにあります。GBASPでは、使用するボタンの作りが異なるため、スイッチバウンスの影響を受けないようです。