見出し画像

割り込みから始まるマイコン時代のプログラム

その昔、マイコンをイジろうとすれば出来合いのものなんてなかなか無くて、回路図を書いて基板を作り(もしくはユニバーサル基板を使って)、そこに部品をハンダ付けして(高価なチップにはソケットを)、なんとかCPUとRAM、そしてたくさんのスイッチを接続して使えるようにしていました。

そしてプログラムは、そのスイッチを使って入力して走らせていたのです。電源を切ってもデータが消えないROMは容易に使えたのですが、そこにデータを書き込むには装置が必要ですし、書き込むデータを作成するにはデバッグ済みのコードが必要なんで、最初からそんなものは用意できなかったのです。つまり電源をいれれば毎回まっさらな状態から始めることになります。

CPUはリセット信号を受け取ると特定のメモリの値を読み出して、そこからプログラムを実行し始めます。ですから最初にメモリをCPUから切り離してスイッチを使ってメモリにプログラムを書き込んでいくのです。

代表的な8ビットCPUであるZ-80の場合、リセット信号を受け取るとプログラムカウンタ(PC)を0にします。ですからプログラムは0番地から始めれば良いので、アドレスのスイッチを0にして、データをスイッチで文字通り1ビットずつ設定し書き込んでいきます。アドレスをひとつずつ増やしてすべてのプログラムデータを書き込んだらスイッチを戻してCPUにバトンを渡しリセットをすればプログラムが走ります。

伝説の編集者が蘇らせる古のパソコンを見た
https://pc.watch.impress.co.jp/docs/column/musashino_proto/642592.html

Z-80の場合、0番地に続いてIRQやNMIといった信号を受け取った時にPCにセットされるアドレスが続くので、0番地に書き込む最初に実行するコードはだいたいにおいて、少し後ろのメモリへのJP命令になります。

毎回、こんなことを繰り返すのも大変なので、電源を入れたらROMにあるプログラムを実行するように、最初に走るプログラムを作り始めます。今で言うファームウェアを用意するわけです。

作りにもよるのですが、IRQやNMIはキーボードなどの他のデバイスに接続されており、例えばキーが押されたら発生するようになっています。これらは「割り込み(インタラプト)」と呼ばれており、この信号を受け取ると、それまで実行していたプログラムを中断し、信号に応じた値をPCに読み込みます。そして「割り込み処理」と呼ばれるプログラムを実行し、キーからの割り込みであれば、押されたキーのコードを特定のメモリなどにしまい、必要であればフラグなども立ててRTIなどの命令で中断していた元のプログラムの実行を再開します。

Z80割り込み処理について

こうしてプログラムを実行しているときでも、キーを入力したり、キーによって走っているプログラムを停止させたりできるわけです。

こういったプログラムを書いて、完成したら晴れてROMに書き込んで、電源を入れたら使えるようになると、毎回イチからプログラムを入れる必要はなくなります。

こういった理由でZ-80など80系のCPUは、ROMが0番地から使われます。68系(6502も)では最初に実行するプログラムの番地がROMに書いてあり、その番地から処理が始まるのですが、書いてある番地がメモリの最後の方(0xFFFFに近い)にあるので、ROMは一番後ろの方のアドレスに置かれます。

プログラムをファームから書き始めたような人は、こうして「割り込み処理」の考え方になれているので、OSが擬似的にCPUがたくさんあるように見せかけるマルチタスクがどのように実現されているかは、さほど違和感も無く覚えることが出来ます。デバイスドライバなんかも、デバイスからのイベントを受け取って動作するものですから、割り込みの一種みたいなものです。

もちろんOSがちゃんと走っているパソコンであっても、この基本原理は何も変わりません。直接デバイスを扱うようなマシン語のプログラムを書こうと思えば、この割り込みたちと付き合うことになります。割り込みはハードウェアだけの特権ではなく、ソフトウェア的にも特定の命令を実行して、同じような処理を書くこともできます。

プログラムを覚えて書けるようになった人でも、この「割り込み」の考え方がなかなか身につかないことが多いようですが、こうして「そもそも」の部分がわかれば、そんなに悩まないでも理解できるように思います。

割り込み処理では、まず割り込みの原因を調べて、それに応じた処理を行います。それまで走っていたプログラムで使っていたメモリは不用意に変更すると、影響が出てしまうので、決まったアドレスを使うようなサブルーチンは使えません。それに素早く処理を終えないと元のプログラムに影響が出るかもしれませんし、もっと大変なのは次の割り込みが発生してしまうかもしれません(次の割り込みを待たせるのか、無視するのか、割り込みをネストするかは、ハードウェアやOSの作り、割り込みの種類によります)。

こういった部分が厄介で、CPU自身がこの手の処理のバグ(もしくは制限または仕様)を持っていたりすることもあったので、割り込み処理は命令の実行時間を数えたりして書く必要もありました。マルチタスクが用意されている場合は、特定のフラグを立てるだけで処理を終えて、処理するタスクが実行されるようにし、後は普通のプログラムとして処理するのが吉です。

以前はここまでで良かったのですが、今は複数のCPUが用意されていて本当に同時にプログラムが走るので、なかなかキツイことになりました。ここは割り込みを禁止しているので、メモリの値は書き換えられないだろうとタカをくくっていたコードを(NMIのことは忘れることにしています)見直さないと、他のCPUが値を変更してしまうかもしれません。ちゃんと保護命令を使わないとならないわけです。

今ではマルチタスクが当たり前で、ソフトウェア的にも並列処理に対応することが求められるので、実際に同時に実行されるか、同時に実行されるように見えるかは別にして、それぞれの処理の時間であるとかリソースの保護への配慮を考えることが多くなりました。今まで真っ直ぐなプログラムしか書いたことが無いと、なかなかハードルがあると思うので、まずは簡単な「割り込み処理」を試して「そもそも」を実際にやってみるのが時代を乗り切るポイントです。

ヘッダ画像は、CQ出版「私だけのマイコン設計&製作」(昭和52年末初版、まだISBNコードもない)P21より。

愛読しました

#マイコン #割り込み #ファームウエア #マルチタスク

この記事が気に入ったらサポートをしてみませんか?