標準TTLだけ(!)でCPUをつくろう!(組立てキットです!)
(ホントは74HC、CMOSなんだけど…)
[第242回]

●割込みとは?

前回は、TK80のステップ動作について説明をしました。
ステップ動作には割込みが利用されています。
モニタプログラムのリストもお見せしましたが、プログラムについての説明はしていませんので、「なにがなんだかさっぱりわからんではないか」と言われてしまうかも知れません。

どうも説明の順番がめちゃくちゃで、というか、ことの成り行きでこういうことになってしまいましたので、ほんとうは、モニタプログラムのリストはもっとあとの方でお見せするはずだったのです。
ですので、プログラムについての説明は、もう少しあとまで、いましばらくお待ち下さい。
とにかく、まずは割込みについての説明を片付けてしまいましょう。
そうしないことには、とにかく話が先に進みません。

で、「割込み」とは?というお話です。

たとえば、何か作業をしている、としましょう。
なんでもいいです。
CPU君は、言われたとおりの仕事をしています。
これが通常のプログラムの実行です。

すると、そこに電話がかかってきました。
お得意様からです。
「わるいけど、急ぎで配達してもらいたいものがあるんだけど、持ってきてくれない?」
お得意様からの注文ですから、断るわけには行きません。
CPU君は、今していた作業を中断して、先にお得意様からの仕事を片付けます。
これが「割込み」です。
お得意様からの仕事が片付いたら、またさきほど中断した仕事の続きから作業を再開します。

お得意様からの電話はいつかかるかわかりません。
でも、かかってきたら、とにかくただちに応答しなければなりません。

しかし、状況によっては、応答できない場合もあります。
たとえば、てんぷらを揚げている真っ最中、などというときには、目を離せませんから、電話に出ることができません。

電車の中とか、病院などでは、携帯の電源は切らなくてはいけません。当然電話に出ることができません。

●EI(Enable Interrupt)とDI(Disable Interrupt)

CPUには電話はありませんから(当たり前です)、代わりにINT入力端子に信号を入れます。
Hアクティブの信号を入れるCPUもあれば、Lアクティブの信号を入れるCPUもあります。
8080では、命令の実行サイクルの特定のクロックのときに、信号の状態をチェックします。ですから割込み信号はエッジではなくてレベル(ある幅をもった信号)ということになります(信号幅については、またのちほど説明をします)。

都合が悪いときには携帯の電源を切ってしまうように、今は都合が悪いから、割込み信号が入ってきても、応答しませんよ、ということで、割込み信号の受け付け回路を遮断してしまうための命令が用意されています。
それがDI(Disable Interrupt)です。

逆に、今は大丈夫だから、INT信号が入ってきたら、割込みプログラムを実行しなさいよ、という場合にはEI(Enable Interrupt)命令を使います。
EIが実行されると、それ以後は、INT信号が入力されると、割込みプログラムが実行されます。

●割込みプログラム

8080での割込みの仕組みは、本当は難しくて、外部回路が必要です。
INT信号を入れるだけではなくて、それと同時に、ある特定のタイミングで外部データバスに8ビットの命令コードを入れる必要があります。
この命令コードは、8ビット1バイトのコードならばなんでもよいのですが、割込みの目的からして、RST命令を入れることになります。

RST命令については、[第217回]で説明をしました。
1バイトのCALL命令です。
通常のCALL命令とは違って、プログラムの開始アドレスが最初から決まっています。
RST0〜RST7の8種あります。
RST0の開始アドレスは0000で、RST1が0008、というように8バイト単位で並んでいて、最後のRST7は0038になります。
この最後のRST7はマシン語コードがFF(11111111)で、データバスに何も入れない場合には、このコードになることから、実質的にはただINT信号を入力するだけで、割込みプログラムを実行させることができます。

8080では、そのように特別の外部回路が不要な割り込みとして、もっとも普通にRST7が利用されます。
INT信号だけを入力した場合には、RST7が命令されたことになりますから、CPUがEI命令を実行したあとで、割込みが受け付け可能状態になっていれば、割込みが発生し、0038からのプログラムが実行されることになります。

0038から割込みプログラムを書いてしまっても全く構わないのですが、割込みプログラムというのは、普通の処理ではない、特別の処理の場合が多いので、割込みプログラムは通常の処理プログラムの後ろの方に書くことが多いようです。
そして0038には、その割込みプログラムへのJMP命令だけを書いておきます。

参考までに、前回([第241回])のTK80モニタプログラムを見ていただければ、そのようになっていることがわかります。
0038には、C35101 JMP BRENT があって、そのジャンプ先、0151から割込み処理プログラム(BRENT)が始まっています。

割込みが受け付けられると、それ以後、新たな割込みは受付けられません。
つまり勝手にDI命令が実行された状態になります。

通常は、INT信号の信号幅として、必ず受付けられるための十分な時間を(しかもアバウトな時間幅)アクティブにするので、もしこういう仕組みになっていないと、割込みプログラムの実行開始直後に、また割込みが発生してしまい、それがINT信号がなくなるまで続いてしまうことになって、使い物にならなくなります。

そこで割込みが受付けられると、それ以後INT信号がまだ継続してアクティブであっても、重ねて割込みが受け付けられないように、自動的に割込み禁止状態になるような回路になっています。
したがって割込みプログラムの実行中に、複数回のINT信号が入力された場合でも、その信号は無視されます。
お、お、お。
そうでした。あの、電話の「お話中」と全く同じですよね。

しかし、すると、割込みプログラムの処理が終わって、メインプログラムに戻ったら、自動的に割込みが再び受付け可能になるか、というと、8080ではそのような仕組みにはなっていません。
割込みが受付けられると同時に、割込み禁止状態になるのですが、それを再び割込み可能にするためには、プログラムの中でEI命令を実行しなければならないのです。

なおCPUがリセットされると、割込みは禁止状態になります。
つまり、CPUが0000から実行を開始した初期状態では、割込みは受け付け禁止状態になっていますから、割込みを可能にしたいときには、その時点で、まずEI命令が実行されるようにプログラムを書く必要があります。

そして、通常は、次の割込みを受付け可能にするために、割込みプログラムの最後のRET命令の直前に、EI命令を書きます。
あ。割込みプログラムは強制的に実行される「サブルーチン」ですから、最後はRET命令で終わります。

ところで、TK80モニタプログラムの割込み処理ルーチンを見てみますと…。
あれ?どういうわけか、最後がRET命令ではありません。


              ;
              ; BREAK ENTRY 
              ; BREAK & ONE STEP OPERATION
              ;
                ORG $0151
              ;
0151 E3       BRENT:XTHL
0152 22E0FF     SHLD PSAVE
0155 F5         PUSH PSW
0156 210400     LXI H,$0004
0159 39         DAD SP
015A F1         POP PSW
015B 22E2FF     SHLD SSAVE
015E E1         POP H
015F 31ECFF     LXI SP,DATA
0162 F5         PUSH PSW
0163 C5         PUSH B
0164 D5         PUSH D
0165 E5         PUSH H
0166 31D1FF     LXI SP,MONSP
0169 3AF2FF     LDA BRKCT
016C A7         ANA A
016D CA8B01     JZ BSTOP
0170 2AF0FF     LHLD BRKAD
0173 EB         XCHG
0174 2AE0FF     LHLD PSAVE
0177 7D         MOV A,L
0178 BB         CMP E
0179 C28501     JNZ NOBRK
017C 7C         MOV A,H
017D BA         CMP D
017E C28501     JNZ NOBRK
0181 21F2FF     LXI H,BRKCT
0184 35         DCR M
0185 CD9101   NOBRK:CALL ADDSP
0188 C3F901     JMP RESRG
018B CD9101   BSTOP:CALL ADDSP
018E C35100     JMP START
0191 2AEAFF   ADDSP:LHLD FSAVE
0194 22ECFF     SHLD DATA
0197 2AE0FF     LHLD PSAVE
019A 22EEFF     SHLD ADRES
019D CDA101     CALL RGDSP
01A0 C9         RET
えーっ。最後はRETだよー。
いーえ。よーく、見てください。

最後のRETは、割込みプログラムの中でCALLされているサブルーチンADDSPの終りのRETなのです。
割込みプログラムの最後は2つあります。
その上のJMP RESRGとJMP STARTです。

実はTK80の割込みプログラムは、一般的な割込みプログラムと使い方が異なっていて、特殊な使い方をしているため、最後がRETではなくて、JMPになっているのです。
でもJMP命令の先をたどっていくと、RET命令に行きつきます。

どこで次の割込みが受付けられるようにしているのでしょう?

ほーら、こんなところにありました。

              ;
              ; REGISTER RESTORE
              ;
01F9 2AE2FF   RESRG:LHLD SSAVE
01FC F9         SPHL
01FD 2AE0FF     LHLD PSAVE
0200 E5         PUSH H
0201 2AE4FF     LHLD LSAVE
0204 E5         PUSH H
0205 2AEAFF     LHLD FSAVE
0208 E5         PUSH H
0209 2AE8FF     LHLD CSAVE
020C 4D         MOV C,L
020D 44         MOV B,H
020E 2AE6FF     LHLD ESAVE
0211 EB         XCHG
0212 F1         POP PSW
0213 E1         POP H
0214 FB         EI
0215 C9         RET

確かに、最後はRETになっていて、その前にEIがあります。
実は、このルーチンは[RET]キーが押されたときに実行されるルーチンでもあるのです。
うーん。どうなっているのか、ちょっと、難しいですねー。
ま。プログラムの説明は、難しいですから、ちょいとあとにしておきましょう。

●割込みが受付けられるタイミング

割込みが受付け可能になっていたとしても、INT信号が入ってきたら、いつでも割込みが受付けられる、というわけにはいきません。
たとえば命令の実行の途中で割込みプログラムが実行されてしまって、レジスタの中身が、命令が開始されたときと終了したときで変ってしまったりしたら、もうめちゃくちゃになってしまいます。
ですから、INT信号はいつ入ってくるかわかりませんが、その信号が受付けられて、割込みプログラムがCALLされるのは、今実行中の命令が完了してから、ということでなければならない、ということになります。

8080のマニュアルには、INT信号は「各命令の最後のマシンステートメントの時に」受け付けられる、と書いてあります。
8080の命令は実行にかかるマシンクロックが命令によって異なっており、当然命令によって、最後のクロックは異なっています。
8080の内部回路では、各命令の最後のステートメントを区別する回路が組んであったのだろう、と思います。
でも「つくるCPU」の回路では、そのような特別の仕組みは考慮していません。

どうやったら、「命令の最後」のタイミングをみつけることができるでしょうか?

「つくるCPU」の回路では、最も簡単で確実なタイミングとして、その命令サイクルが始まった直後のT0のタイミングでINT信号を認識するように考えました。
逆転の発想です。
このタイミングなら、その直前の命令がどんな長さの命令でも、必ず実行は完了しています(わかりますよね)。
そして、次の命令のサイクルは始まっていますが、まだメモリから命令の読み込みを開始したばかりで、まだOPコードレジスタにはラッチされていない、というタイミングですから、実質的には、次の命令はまだ実行開始前の状態です。
OPコードがラッチされるのはT1で、実際に命令の回路がアクティブになるのはT4からです。

ところで、このタイミングでINT信号を受け付ければ、通常は問題がないはずなのですが、それではまずい場合がでてきます。
それはEI命令が実行された直後に発生します。
さきほど、TK80モニタプログラムの割込みルーチンの終りについて説明をしました。
最後はこうなっていました。
0214 FB EI
0215 C9 RET


もしも、EIが実行されて、割り込みが受け付け可能になった直後、つまり次のRET命令のT0のタイミングでINT信号がアクティブになったとすると、割込み処理プログラムが終わるためのRET命令が実行される前に、割り込みが発生してしまうのではないか?

それでは、都合が悪い、はずです。
なぜなら、割込みが二重に発生してしまうからです(8080のように命令の最後のステートメントでINTが受け付けられる、ということでも、問題は同じです)。

8080の開発者は、当然このことに気がついたはずです。
そのために、EI命令の回路には、特別の工夫がされています。

というあたりで、本日は終り、です。この続きはまた次回にすることにいたします。
2009.6.6upload

前へ
次へ
ホームページトップへ戻る