MOS Technology 6502とその派生製品であるWDC 65C02、WDC 65C802、WDC 65C816、およびCSG 65CE02からなる65xx ファミリのマイクロプロセッサはすべて、同様の方法で割り込みを処理します。すべての 65xx プロセッサに共通する3 つのハードウェア割り込み信号と、1 つのソフトウェア割り込みであるBRK命令があります。WDC 65C816では、仮想メモリ アーキテクチャの実装に役立つ4 番目のハードウェア割り込みであるABORTと、何らかのタイプのコプロセッサ (たとえば、浮動小数点プロセッサ) を備えたシステムで使用することを目的とした COPソフトウェア割り込み命令( 65C802 にも存在する) が追加されています。[ 1 ] [ 2 ]
ハードウェア割り込み信号はすべてアクティブローであり、以下のとおりです。[ 1 ]
RESET信号が検出されると、プロセッサは6クロックサイクルのシステム初期化期間に入り、その後、ステータスレジスタの割り込み要求無効フラグを設定し、実行を開始する前にプロセッサ初期化ベクタ($00FFFC ~$00FFFD )に格納されている値をプログラムカウンタにロードします。[ 1 ] ネイティブモードで動作している場合、65C816/65C802はエミュレーションモードに切り替わり、ソフトウェア制御でネイティブモードに戻されるまでその状態を維持します。
NMIまたはIRQ信号の検出、およびBRK命令の実行は、次の順序で同じ一連のイベントを引き起こします。[ 1 ] [ 3 ]
ABORT信号がアサートされた場合の65C816の動作は、上記の説明とはいくつかの点で異なるため、以下で別途説明します。
プロセッサはアキュムレータとインデックスレジスタをスタックにプッシュしないことに注意してください。割り込みハンドラ内のコードがそのタスクを実行し、必要に応じて割り込み処理の終了時にレジスタを復元する必要があります。また、 8ビットの65xxプロセッサすべて、およびエミュレーションモードで動作している65C802/65C816では、 IRQのベクタはBRKのベクタと同じであることにも注意してください。ネイティブモードで動作している場合、65C802/65C816はIRQとBRK用に別々のベクタを提供します。[ 4 ]
割り込み要求無効フラグ (ステータス レジスタのIビット) が設定されると、 IRQ信号の検出は無効になりますが、他の割り込みには影響しません (ただし、 WDC CMOS プロセッサで実装されているWAI命令に関する以下のセクションを参照してください)。さらに、65(c)02 または 65C816/65C802 がエミュレーション モードで動作している場合、スタックにプッシュされるステータス レジスタのコピーは、割り込みの原因がBRK (ソフトウェア割り込み)の場合はBフラグがセットされ、 IRQ が原因の場合はクリアされます。[ nb 4 ] したがって、割り込みサービス ルーチンは、スタックにプッシュされた保存済みステータス レジスタのコピーを取得し、Bフラグの状態を確認してIRQとBRKを区別する必要があります。[ 1 ] [ 2 ] [ 4 ] 65C802/65C816 をネイティブ モードで動作させると、2 つの割り込みタイプに別々のベクタがあるため、この要件は不要になります。[ 2 ]
65C816のABORTB割り込み入力は、ページフォルトやメモリアクセス違反などのハードウェア例外が検出された際に、プログラムの実行をリダイレクトする手段を提供することを目的としています。そのため、 ABORTB入力がアサート(否定)された場合のプロセッサの応答は、IRQBやNMIBがアサートされた場合とは異なります。また、 ABORTBに正しく応答するには、マシンサイクルの適切なタイミングで割り込みが発生する必要がありますが、 IRQBやNMIBにはそのような要件はありません。
有効なメモリ サイクル中にABORTBがアサートされると、つまりプロセッサがVDAおよび/またはVPAステータス出力をアサートすると、次の一連のイベントが発生します。[ 2 ]
スタックにプッシュされるアドレスはプログラムカウンタの内容ではなく、中止された命令のアドレスであるため、ABORT割り込みの後にRTI(割り込みからの復帰)を実行すると、プロセッサは他の割り込みの場合のように次の命令ではなく、中止された命令に戻ります。
プロセッサがアボートに正しく応答するためには、有効なアドレスがバス上に配置され、そのアドレスがページフォルト、メモリアクセス違反、またはその他の異常 (特権命令の実行試行など) であると判断された時点で、システムロジックはABORTB入力をアサート (否定) する必要があります。したがって、ロジックはプロセッサがVDAまたはVPA信号をアサートするまでABORTBをアサートしてはなりません。また、ABORTB はフェーズ 2 クロックの立ち下がりまでアサートされたままで、その後すぐに解放される必要があります。これらのタイミング制約が守られない場合、アボート割り込みハンドラ自体がアボートされ、レジスタやメモリが定義されていない方法で変更される可能性があります。[ 2 ]
NMOS 6502 およびその派生版 (6510 など)では、ハードウェア割り込みラインの同時アサートとBRKの実行が設計で考慮されていませんでした。このような場合、 BRK命令は無視されます。また、プロセッサステータスレジスタの10 進モードフラグの状態は、あらゆる種類の割り込みの後も変化しません。この動作は、割り込み時にたまたま 10 進モードが有効になっている場合、割り込みハンドラで特定が困難なバグを引き起こす可能性があります。これらの異常は、プロセッサのすべてのCMOSバージョンで修正されました。 [ 2 ]
適切に設計され、簡潔な割り込みハンドラまたは割り込みサービスルーチン(ISR)は、割り込みを引き起こすあらゆるイベントを迅速に処理するだけでなく、割り込みを受けたフォアグラウンドタスクに一切干渉することなく処理を行います。つまり、 ISRは割り込みを受けたタスクに対して「透過的」である必要があります(ただし、特殊なケースでは例外が適用される場合があります)。これは、ISRがマイクロプロセッサ(MPU)の状態を維持し、本来変更すべきでないメモリ内のデータを一切変更しないことを意味します。さらに、ISRは完全なリエントラントである必要があります。つまり、2つの割り込みが連続して発生した場合、ISRは2番目の割り込みの処理が完了した後、最初の割り込みの処理を再開できる必要があります。リエントラント性は通常、ストレージにMPUハードウェアスタックのみを使用することで実現されます(ただし、他の方法も考えられます)。
MPUの状態を保持するということは、ISRが、割り込み発生時にMPUレジスタにあった値がISR終了時にも保持されていることを保証しなければならないことを意味します。保持プロセスの一部は、MPUが割り込みを認識する際に自動的に処理されます。MPUは、ISRを実行する前に、プログラムカウンタ(および65C816/65C802のプログラムバンク)とステータスレジスタをスタックにプッシュします。ISRの完了時にRTI命令が実行されると、MPUは逆の処理を行います。65xxファミリのどのメンバーも、他のレジスタをスタックにプッシュしません。[ 2 ]
ほとんどのISRでは、透過性を確保するためにアキュムレータレジスタおよび/またはインデックスレジスタを保持し、RTIを実行する前の最終ステップとして後で復元する必要があります。65C816/65C802の場合、割り込み発生時にエミュレーションモードで動作しているかネイティブモードで動作しているかを考慮する必要があります。後者の場合、透過性を保証するためにデータバンク(DB)レジスタとダイレクト(ゼロ)ページ(DP)レジスタも保持する必要があるかもしれません。また、65C816ネイティブモードのオペレーティングシステムは、アプリケーションソフトウェアとは異なるスタック位置を使用する可能性があり、その場合、ISRはスタックポインタ(SP )を保持し、その後復元する必要があります。65C816 / 65C802では、ネイティブモードで動作する場合、アキュムレータレジスタとインデックスレジスタのサイズが8ビットまたは16ビットになる可能性があるため、後で復元するためにそのサイズを保持する必要があることがさらに問題を複雑にしています。[ 2 ]
ISR内でMPUの状態が保持および復元される方法は、65xxファミリのバージョンによって異なります。NMOSプロセッサ(6502、6510、8502など)の場合、スタックにプッシュおよびプルできるのはアキュムレータのみであるため、アキュムレータとインデックスレジスタを保持する方法は1つしかありません。[ 5 ] したがって、次のISRエントリコードが一般的です。
PHA ; アキュムレータTXA を保存PHA ; X レジスタTYAを保存PHA ; Y レジスタを保存CLD ; 10 進フラグをクリアしてバイナリ モードを確保CLD命令が必要な理由は、前述のとおり、6502のNMOSバージョンでは、割り込みが発生したときにステータスレジスタのD (10進モード)フラグがクリアされないためです。
アキュムレータレジスタとインデックスレジスタが保持されると、ISRは必要に応じてそれらを使用できます。ISRが処理を完了すると、レジスタを復元し、中断されたフォアグラウンドタスクを再開します。以下に、典型的なNMOSコードを示します。
PLA TAY ; Yレジスタを復元PLA TAX ; Xレジスタを復元PLA ; アキュムレータを復元RTI ; 中断されたタスクを再開RTI命令の結果として、割り込み発生時にMPUが10進数モードであった場合、MPUは10進数モードに戻ります。[ 5 ]
65C02 、およびエミュレーションモードで動作する65C816/65C802は、アキュムレータを介さずにインデックスレジスタをプッシュおよびプルできるため、コード量が少なくて済みます。[ 2 ] また、ISRを実行する前に自動的に10進モードをクリアします。[ 2 ] 以下は典型的な例です。
PHA ; アキュムレータを保存PHX ; Xレジスタを保存PHY ; Yレジスタを保存処理が完了すると、ISRは逆のプロセスを実行します。
PLY ; Yレジスタを復元PLX ; Xレジスタを復元PLA ; アキュムレータを復元RTI ; 中断されたタスクを再開前述のとおり、65C816/65C802 をネイティブモードで動作させる場合、可変レジスタサイズとDBレジスタおよびDPレジスタを考慮する必要があるため、少し複雑になります。インデックス レジスタの場合、サイズに関係なくプッシュできます。サイズを変更すると、これらのレジスタの最上位バイト (MSB) が自動的にゼロに設定され、プッシュされた値が復元されたときにデータが失われることはありません。ただし、インデックス レジスタがプッシュされたときと同じサイズである必要があります。[ 2 ]
ただし、アキュムレータは実際には.Aと.Bという 2 つのレジスタです。[ 2 ] アキュムレータが 8 ビットに設定されているときにプッシュすると.Bは保持されません。[ 2 ] ISR が.Bを何らかの方法で変更すると、透過性が失われる可能性があります。したがって、ISR が.Bを使用する場合は、プッシュまたはプルする前にアキュムレータを常に 16 ビットに設定する必要があります。インデックス レジスタをプッシュする前に 16 ビットに設定する方が効率的です。そうしないと、ISR はステータス レジスタの余分なコピーをプッシュして、スタックからプルする前にレジスタ サイズを復元する必要があります。
ほとんどのISR(情報システム要求)において、以下の入力コードを使用することで透明性の確保という目標を達成できます。
PHB ; 現在のデータバンクを保存PHD ; ダイレクトページポインタを保存REP #%00110000 ; 16ビットレジスタを選択PHA ; アキュムレータを保存PHX ; Xレジスタを保存PHY ; Yレジスタを保存上記のコード断片では、記号%はMOS TechnologyおよびWDC標準アセンブリ言語のビット演算子構文です。
ISRにスタック位置が割り当てられている場合、上記のプッシュ処理の後、メモリ内でスタックポインタ( SP )の保持が行われる必要があります。その理由は明らかでしょう。上記のシーケンスに以下のコードを追加することで、この要件に対応できます。
TSC ; スタックポインタをアキュムレータにコピーSTA stkptr ; 安全なRAMのどこかに保存LDA isrptr ; ISRのスタックポインタを取得 &... TCS ; 新しいスタック位置を設定ISRの完了後、上記のプロセスは以下のように逆方向に実行されます。
REP #%00110000 ; 16ビットレジスタを選択TSC ; ISRのSPを保存... STA isrptr ; 後で使用するためLDA isstkptr ; フォアグラウンドタスクのSPを取得 &... TCS ; 設定PLY ; Yレジスタを復元PLX ; Xレジスタを復元PLA ; アキュムレータを復元PLD ; ダイレクトページポインタを復元PLB ; 現在のデータバンクを復元RTI ; 中断されたタスクを再開RTIを実行すると、65C816/65C802は、以前に保存されたステータスレジスタをプルすると、レジスタサイズビットの両方が割り込み発生時の値に設定またはクリアされるため、レジスタサイズを割り込み発生時の値に自動的に復元することに注意してください。[ 2 ]
ISR内で65C816/65C802をネイティブモードからエミュレーションモードに切り替えることは可能ですが、これは危険を伴います。[ 2 ] アキュムレータレジスタとインデックスレジスタを8ビットに強制する(インデックスレジスタの最上位バイトが失われる)ことに加えて、エミュレーションモードに入るとスタックポインタが8ビットに切り詰められ、スタック自体がページ1 RAMに移動します。[ 2 ] その結果、割り込み発生時に存在していたスタックは、ページ1 RAMにあり、かつ256バイト以下でない限りアクセスできなくなります。一般的に、割り込み処理中にモードを切り替えることは推奨される手順ではありませんが、特定の動作環境では必要になる場合があります。
前述のとおり、BRKとCOPはソフトウェア割り込みであり、そのため、システム機能を実装するためにさまざまな方法で使用できます。
BRKの歴史的な用途の一つは 、システムのファームウェアにバグが発見された際にPROMのパッチ適用を支援することでした。ファームウェア開発における一般的な手法は、BRKベクタをPROM内の未プログラム「パッチ領域」を指すように設定することでした。バグが発見された場合、パッチ適用は、欠陥のある命令が存在するアドレスにあるすべてのヒューズを「消去」することで行われ、これにより命令のオペコードが$00に変更されます。結果として生成されたBRKを実行すると、MPUはパッチ領域にリダイレクトされ、そこに適切なパッチコードが書き込まれます。多くの場合、パッチ領域のコードは「スタックをスニッフィング」してバグが発生したアドレスを特定することから始まり、PROM内に複数のパッチが存在する可能性がありました。EPROMやEEPROMが普及するにつれて、PROMパッチ適用におけるBRKの使用は減少しました。
ソフトウェア開発におけるBRKのもう 1 つの用途は、マシン語モニタと連携したデバッグ補助としてです。オペコードをBRK ( $00 ) で上書きし、BRKハードウェアベクタをモニタのエントリ ポイントに向けることで、プログラムを任意の任意の場所で停止させ、モニタに制御を委ねることができます。その際、メモリを調べたり、プロセッサのレジスタ値を表示したり、コードにパッチを適用したりできます。Kuckes と Thompson が提唱したデバッグは、デバッグ対象プログラムの実際の動作を変更することなくBRK命令に置き換えることができるNOP命令 (オペコード$EA ) をコードに散りばめることで容易になります。[ 5 ] [ 6 ] [ 7 ]
BRKおよびCOP命令の特徴は、プロセッサがどちらの命令も 2 バイトの命令として扱うことです。つまり、オペコード自体と、それに続く「シグネチャ」と呼ばれる 1 バイトです。[ 2 ] BRKまたはCOP を実行すると、プロセッサはプログラム カウンタに 2 を加算してからスタックにプッシュします。したがって、RTI (割り込みからの復帰)が実行されると、中断されたプログラムはシグネチャの直後のアドレスから再開します。BRKをデバッグ デバイスとして使用する場合、実行が期待どおりに再開されるように、プログラム カウンタをシグネチャを指すように調整する必要がある場合があります。あるいは、シグネチャの「プレースホルダ」としてNOPを挿入することもできます。この場合、プログラム カウンタの調整は不要です。
BRKおよびCOP 命令がスタックにプッシュする前にプログラムカウンタを 2 倍インクリメントするという事実は、一部のメインフレームコンピュータに見られるように、これらをスーパーバイザ呼び出し命令として扱う手法を容易にします。通常の手順は、シグネチャをオペレーティングシステムのサービス インデックスとして扱うことです。オペレーティングシステムのBRKまたはCOPハンドラは、スタックにプッシュされたプログラムカウンタの値を取得し、それをデクリメントして、結果として得られるメモリ位置から読み取ってシグネチャを取得します。[ 8 ] [ 9 ] シグネチャをゼロベースのインデックスに変換した後、単純なルックアップ テーブルを参照して、適切なサービス ルーチンのアドレスをプログラム カウンタにロードできます。サービス ルーチンが完了すると、RTI命令を使用して、オペレーティングシステム呼び出しを行ったプログラムに制御を戻します。BRKのシグネチャは任意の値にすることができますが、 COPのシグネチャは$00 - $7Fの範囲に制限する必要があることに注意してください。[ 2 ]
BRKおよび/またはCOPを使用してオペレーティングシステム サービスを要求すると、ユーザー アプリケーションは各オペレーティングシステム 関数のエントリ アドレスを知る必要がなく、目的の操作を呼び出すための正しいシグネチャ バイトだけを知るだけで済みます。したがって、メモリ内でのオペレーティングシステムの再配置によって、既存のユーザー アプリケーションとの互換性が損なわれることはありません。また、BRKまたはCOPを実行するとプロセッサは常に同じアドレスに移動する可能性があるため、要求されたサービスに制御を渡す前にスタック上のレジスタを保持するための簡単なコードを使用できます。ただし、このプログラミング モデルでは、サービスをサブルーチンとして呼び出す場合と比較して、実行速度がやや遅くなります。これは主に、割り込みが発生するたびにスタック アクティビティが発生するためです。また、 BRKまたはCOPを実行すると割り込み要求が無効になるため、オペレーティングシステムで割り込み要求を再度有効にする必要があります。
WAI ( WA it for Interrupt、オペコード$CB ) は、65C02 および 65C816/65C802 マイクロプロセッサ (MPU) の WDC バージョンで使用可能な命令で、MPU を停止し、何らかのハードウェア割り込みが発生するまで半カタトニック状態にします。 [ 2 ] WAIの主な用途は、MPU が予期されるイベントが発生するまで何もする必要がなく、システムが待機している間の消費電力を最小限に抑えたい場合や、迅速な応答が必要な場合など、低消費電力の組み込みシステムです。WAIを使用する典型的なコードの例は次のとおりです。
SEI ; IRQを無効にするWAI ; ハードウェア割り込みを待機する; ...ここから実行が再開されます上記のコード断片では、MPUはWAI命令の実行時に停止し、非常に低消費電力の状態になります。WAI命令の前に割り込み要求(IRQ)が無効になっているにもかかわらず、 MPUは待機中にハードウェア割り込みに応答します。IRQを受信すると、MPUは1クロックサイクルで「起動」し、 WAI命令の直後の命令から実行を再開します。したがって、割り込みレイテンシは非常に短く(14メガヘルツで70ナノ秒)、外部イベントに対して可能な限り最速の応答を実現します。
WAIといくつかの点で類似しているのがSTP ( ST o P、オペコード$DB ) 命令で、単一の割り込み入力を待っている間、MPU を完全にシャットダウンします。[ 2 ] STPが実行されると、MPU は内部クロックをハイフェーズで停止し、レジスタ内のすべてのデータを保持して、低電力状態に入ります。MPU は、リセット入力ピン (割り込み入力として分類されるRESB ) をローにすることで、この状態から復帰します。その後、ハードウェア リセット ベクタであるアドレス$00FFFC-$00FFFDに格納されているアドレスから実行が再開されます。WAI と同様に、STP は、 MPUの注意を必要とするイベントの間隔が長く、他の処理が不要な低電力組み込みアプリケーションで使用することを目的としています。STPは、処理が完全に停止してしまうため、通常のプログラミングでは使用されません。