8−7.割り込みとは?
割り込みというのは別にマイコン独自の考え方というわけではなく、生活の中でもよくあることです。たとえば部屋の掃除をしている最中に電話がかかってくると、いったん掃除をやめて電話に出て、電話が終わるとまた掃除の続きを始めますよね。これが割り込みの考え方です。つまり外部の要因でMPUの命令実行をいったん中断させ、別のプログラムを実行させてから元のプログラムに戻るわけです。
8−8.Z80 CTC
さて、それでZ80の割り込みシステムですが、これは高度なことが出来る代わりにかなり複雑な物になっています。そのため普通はZ80ファミリと呼ばれるICを使用します。今まで触れませんでしたが、Z80ファミリのICには
Z80 CPU(Z80本体)
Z80 PIO(Parallel I/O, 8255と用途は同じ。多用します)
Z80 CTC(Counter Timer Circuit, 時間を測るために使う。これも多用します)
Z80 SIO(Serial I/O, パソコンとRS232Cを通じて通信するとき等に使います)
Z80 DMA(Direct Memory Accessをするときに使われていました)
などがあります。しかし、私がよく使用する秋月電子通商のAKI-80と言うボードに乗っている、東芝TMPZ84C015というICには、このうち上の4つがすでに内蔵されています。このため特に外付けする必要がないので、大変便利です。ここではこのAKI-80を使用することを前提にお話しします。
では、Z80の割り込みシステムについて、大まかに説明しておきましょう。割り込みにはINT端子が0になると発生するようになっているため、外部のICから割り込みを発生させるときはここにそのICの割り込み出力をつなぐ必要があります。ただしTMPZ84C015では内部ですでに接続がされているのでこれは考えなくても大丈夫です。さて、Z80の割り込みには、実は3種類あります。これを切り替えるにはIM命令を使い
IM 1
のようにします。IMのあとの数字は割り込みモードで、0〜2まであります。このうちモード0は使わないので省略します。モード1は簡単で、割り込みが発生するとメモリの0038H番地にジャンプします。簡単な代わりに、1種類の割り込みにしか対応できません。Z80らしい使い方が出来るのは次のモード2で、これは少々難しいのですが、なるべく簡単に説明しましょう。
以前レジスタの話をしたときにIレジスタというものがあると言ったのを覚えているでしょうか?これが実は重要な鍵を握っています。またモード2割り込みをする周辺ICには必ず割り込みベクタ設定というものがあり、この2つで飛び先アドレスが決定されます。たとえばIレジスタの中身が00Hで割り込みベクタがD0Hだったとすると、メモリの00D0H番地が指定されたことになります。
さて、では割り込みプログラムはこの00D0H番地から実行されるのかというと、そうではありません。この00D0番地には、さらに別の番地のアドレスを入れておかなければなりません。そしてここに入っている番地のアドレスが次に実行される割り込みプログラムの先頭アドレスになるわけです。・・・少し混乱してきたかもしれませんね。もう一度おさらいすると、Iレジスタを上位8ビット、周辺ICの初期化の時設定される割り込みベクタを 下位8ビットとしたアドレス、たとえば00D0H番地に入っているデータ、たとえば0300Hなら0300H番地から割り込みプログラムがスタートするということです。そして割り込みが終わると、最後はRETI命令で元のプログラムに戻ります。普通のサブルーチン用のRET命令とは異なることに注意して下さい。
このように、割り込みはかなり難しそうに思えるかもしれませんが、こちらに雛形を用意しておきましたので、この160行目以降に割り込みプログラムの先頭アドレスを書き込めば終わりです。
あと割り込みで注意しなければならないのはレジスタの扱いです。割り込みプログラムでレジスタをむちゃくちゃにして、割り込み前の元のプログラムに戻ると、暴走する危険があるので、割り込みプログラムの先頭では、そのプログラムで使うレジスタをあらかじめ待避させておかなければなりません。そしてプログラムの終わりでそれを復帰させる必要があります。これにはPUSH/POP命令が便利です。では次から、Z80ファミリについて見ていきましょう。
CTC(Counter Timer Circuit)にはその名の通り、タイマモードとカウンタモードがあります。どちらも基本的にはカウンタなのですが、タイマモードはCPUのクロック入力を数え、カウンタモードは外部入力されたクロックを数えるという違いがあります。
8−9.Z80 PIO
CTCには全部で4チャネルのカウンタがついていて、それぞれ0〜3という番号がついています。I/Oアドレスは10〜13です。そしてここに設定をしていきます。まずはタイマモードの設定です。次のようになります。
bit7:割り込み可・否(割り込み可なら1、不可なら0)
bit6:モード(カウンタモードなら1、タイマモードなら0)
bit5:レンジ(タイマモード時のプリスケーラ、1なら1/256、0なら1/16)
bit4:スロープ(トリガの極性。1で立ち上がり、0で立ち下がりが有効)
bit3:トリガ(タイマモードで外部トリガを有効にするときは1)
bit2:ロード・タイム・コンスタント(この次にタイマコンスタントを設定するなら1)
bit1:リセット(1で動作停止)
bit0:1
まず割り込み可・否はそのままの意味です。
モードは先ほど言ったようにCPUのクロックを数えるならタイマモードです。
レンジはそのCPUのクロックをあらかじめ何分の1にしておくか、という設定です。CPUのクロックでは速すぎるとき、ここで調整します。
スロープはクロックの立ち上がりでカウントするか立ち下がりでカウントするかの設定です。
トリガは、カウントの開始をトリガ入力が入ってからにするかどうかの設定。
ロード・タイム・コンスタントはいくつパルスを数えるかのデータを、この次に設定するかどうかです。
リセットはわかりますよね。
そして普通はロード・タイム・コンスタントを1にするのでこの後続いてその設定をします。00H〜FFHの間で指定して下さい。
それでは1秒ごとに割り込みをかける方法を考えてみましょう。まずch0を使用するとします。そしてまず割り込み可・タイマモード・1/256プリスケーラ・立ち上がり・外部トリガなし・タイムコンスタント設定・セットとしますので10110101,つまりB5Hになります。そしてタイムコンスタントでうまく1秒になるように設定するわけです。
しかしここで困った問題が発生します。AKI-80のCPUクロックは9.8304MHzなので、プリスケーラで1/256すると38.4kHzになります。しかしタイムコンスタントが最大でも00H(FFHではなく00Hが最大です)の256なのでどうがんばっても150Hz(6.6mS)にしかならないのです。これは困った、ということで解決法はないものでしょうか?
答えをいうと、もう1つチャネルを使えばいいのです。ですからch0の出力(ZC/TO1)をch1のクロック入力(CLK/TRG1)に入れます。そしてこちらは割り込み可・カウンタモード・プリスケーラなし・立ち上がり・外部トリガなし・タイムコンスタント設定・セットで11010101でD5Hに設定します。そしてその後タイムコンスタントを150(96H)にすれば見事に1秒ごとに割り込みがかかるはずです。がその前にch0の方はさっき割り込みありにしましたが、こちらで割り込みが6.6mSごとに発生してはまずいので、B5Hを35Hにしておいてください。
割り込みの間隔を変更したり、いろいろ設定を変えたり、いろいろ応用は考えられますが、あとは各自が考えてみて下さい。
Z80 PIOは、基本的に8255と機能は同じです。ただモード2割り込みが使いやすいようになっているので、データ入力はPIOで、出力は8255で、と分けて使うのがいいかもしれません。
8−10.割り込み許可・禁止
PIOには、AポートとBポートの2つのポートがあり、それぞれ8ビットです。 I/Oアドレスは、
Aポートデータ:1CH
Aポートコントロール:1DH
Bポートデータ:1EH
Bポートコントロール:1FH
です。ポートの設定は、各コントロールポートにデータを送ればいいのですが、先ほどの雛形を使うなら、37行目以降を変更するだけでOKです。では設定方法を書きます。まずは動作モードの設定です。
bit7:1(モード設定)
bit6:1(モード設定)
bit5:0(未使用)
bit4:0(未使用)
bit3:1(モード設定時は1)
bit2:1(モード設定時は1)
bit1:1(モード設定時は1)
bit0:1(モード設定時は1)
さて、ここで意味のあるのはbit7と6です。これは、
bit7 bit6 モード 0 0 バイト出力モード 0 1 バイト入力モード 1 0 バイト双方向バスモード 1 1 ビット制御モード
という具合に設定します。が、ビット制御モード以外はあんまり使わないと思うので、ここではビット制御モードについてだけ解説します。その他のモードについてはZ80関係の文献を参照して下さい。
次に、各ビットを入力にするか出力にするかを設定します。
bit7:出力なら0、入力なら1
bit6:出力なら0、入力なら1
bit5:出力なら0、入力なら1
bit4:出力なら0、入力なら1
bit3:出力なら0、入力なら1
bit2:出力なら0、入力なら1
bit1:出力なら0、入力なら1
bit0:出力なら0、入力なら1
これを同じコントロールポートに送ります。その次に割り込み制御語の設定です。
bit7:割り込み可・否(割り込み可なら1、不可なら0)
bit6:割り込み条件(ORなら1、ANDなら0)
bit5:レベル条件(Lなら0,Hなら1)
bit4:マスク指定(しないなら0、するなら1)
bit3:0(割り込み制御語なら0)
bit2:1(割り込み制御語なら1)
bit1:1(割り込み制御語なら1)
bit0:1(割り込み制御語なら1)
これは少し解説しないとわからないと思いますので、解説します。割り込み可・否はわかると思うので省略します。問題はその後の3つです。これはどういう条件の時に割り込みを発生させるかの設定です。まずマスク指定から説明します。マスク指定では、ポートの8ビットのうち、どのビットを監視するのかという設定をこの次に行うかどうかを選択します。例えば下位4ビットだけ監視するとして話を進めます。レベル条件は0になるのを監視するか、1になるのを監視するかの設定です。次に割り込み条件ですが、ORならば、この監視している4ビットの内、どれか1つでもレベル条件に一致すれば、割り込みがかかります。ANDならば、4ビット全部がレベル条件に一致してはじめて、割り込みがかかります。これはAND演算、OR演算を思い出せばわかると思います。
さて、もし割り込み制御語でマスク指定有りにしたならば、次はマスク語の設定です。ここでマスクしていないビットだけが監視されることになります。
bit7:マスクしないなら0、マスクするなら1
bit6:マスクしないなら0、マスクするなら1
bit5:マスクしないなら0、マスクするなら1
bit4:マスクしないなら0、マスクするなら1
bit3:マスクしないなら0、マスクするなら1
bit2:マスクしないなら0、マスクするなら1
bit1:マスクしないなら0、マスクするなら1
bit0:マスクしないなら0、マスクするなら1
こうなります。本当はこのあとに割り込みベクタを設定するのですが、雛形をそのまま使うならば触る必要はないので、割愛します。
割り込みはいつ起こるか、プログラムの側からはわからないため、割り込まれて欲しくないときに割り込みがかかることもあります。たとえばリセット直後の初期化処理中に割り込まれるととんでもないことになるかもしれません。これを防ぐためにZ80には割り込み許可・禁止命令があります。割り込みを禁止するにはDI(Disable Interrupt)、許可するにはEI(Enable Interrupt)命令を使います。ただし割り込み禁止期間が長いとなかなか割り込めないためにうまく動作しない可能性もあるので、割り込み禁止期間は出来るだけ短い方が良いです。