並列して処理をソフトウェアで処理を実施したい場合があります。
そのときスレッドがなぜ必要なのかを段階を追ってまとめていきたいと思います。

【説明】

一定時間ごとにサービスが実行できるかどうかをチェックするというポーリングをベースにしてプログラムを記述することにします。

●STEP1:サービス1つ実施する
とにかくサービスが実行できるようにしてみます。
1.LEDを点滅させる

int main()
{
...while(1)
...{
......if(0.1s経過したか?)
......{
.........led_turn();
......}
...}
}

int led_turn()
{
...(LEDを点滅させる処理)
}


●STEP2:サービス2つ実施する
実行できるサービスを複数もたせます。
1.LEDを点滅させる
2.1sに1回、液晶パネルに時刻表示を行う★
3.シリアルからのコマンドに応答する★

int main()
{
...while(1)
...{
......if(0.1s経過したか?)
......{
.........led_turn();
......}
......if(1sが経過したか?)
......{
.........write_time();
......}
......if(シリアル受信したか?)
......{
.........command_exec();
......}
...}
}

int led_turn()
{
...(LEDを点滅させる処理)
}
int write_time()
{
...(時刻表示処理)
}
int command_exec()
{
...if(dumpコマンドが指定された)
...{
......dump();
...}
}
int dump()
}
...for(i=0;i...{
......(ダンプ表示)
...}
{


●STEP3:たくさん時間のかかるサービスを中断し他のサービスも実施できるようにします
実行できるサービスを複数もたせます。3.の処理中で実施されるサービスではダンプ出力中に10回に1回の割合で現在の状態を保存しメインループに戻り他1.2.の処理も実施できるようにします。
1.LEDを点滅させる
2.1sに1回、液晶パネルに時刻表示を行う
3.シリアルからのコマンドに応答するして中断して再開も対応★

int main()
{
...int in_dump=0;
...while(1)
...{
......if(0.1s経過したか?)
......{
.........led_turn();
......}
......if(1sが経過したか?)
......{
.........write_time();
......}
......if(in_dump)
......{
.........in_dump = dump(1);
......}
......else if(シリアル受信したか?)
......{
.........in_dump = command_exec(0);
......}
...}
}

int led_turn()
{
...(LEDを点滅させる処理)
}
int write_time()
{
...(時刻表示処理)
}
int command_exec(int in_dump)
{
...if(dumpコマンドが指定された)
...{
......ret = dump(0);
...}
...return ret;
}
int dump(int in_dump)
{
...int i,num;
...static int save_i,save_num;

...if(in_dump)
...{
......i = save_i;
......num = save_num;
......goto loop;
...}
...for(i=0;i...{
loop:
......(ダンプ表示)
....../* 定期的に中断してメインループに戻る。次の再開に備えるために状態を保存 */
......if((i%10)==9)
......{
.........save_i = i;
.........save_num = num;
.........return 1;
......}
...}
...return 0;
}


【問題点】
①他の機能へ処理を明け渡すために、時間のかかる処理に対して中断・再開といった記述を追加する必要がある。
1.今の処理の状態を保存する。
2.再度実施されたとき保存された情報を読み出し続きを実施する。
②プログラムが複雑化するとバグが出やすくなる。要はメインの処理に集中しにくくなる。


【対策の方向性】
①上記の例でいうと「1.LEDを点滅させる」、「2. 1sに1回、液晶パネルに時刻表示を行う」、「3.シリアルからのコマンドに応答する」の定期的な処理の切り替え。
②処理状態の中断、保存、再開の共通化。


【解決策】
実現のための着眼点は前章の割込み処理のときに着目したCPUの特長です。処理状態を保存しておきたいのなら、現在のスタックとレジスタの状態を保存しておけば、処理が再開できるということです。

つまり、各処理の内部で独自に中断、保存、再開を行うのではなく、適当なタイミングで中断させてそのときにスタックとレジスタの状態を保存しておき、また処理を再開できるタイミングでスタックとレジスタの状態を復旧するような処理をすべてのサービスに対して共通的に行えば、処理ごとに対処する必要はなくなるわけです。

これから、その仕様案をKOZOS OSを通して設計・実装していくことになるわけですが、スレッドというキーワードがこれから重要になってきます。
ビジーループでシリアル受信を監視する場合はCPUは常に動作していることになります。

一方、今回作成するプログラムは、割込みをきっかけにしてCPUを動作させる、つまり、普段は省電力モードでCPUの命令実行を停止しCPUの消費電力をおさせておく処理を実現します。

このような処理方法は割込みドリブンまたは割込み駆動などとよばれたり、イベント・ドリブンやイベント駆動などと呼ばれる場合もあります。

割込みドリブンは、応答性についてもメリットがあります。

常に見張りを続けるビジー・ループのものよりも、割込みドリブンにすることで向上します。

ビジーループではレジスタを参照するという動作を繰り返しますが、レジスタ参照と次のレジスタ参照の間に必ず時間的な隙間ができてしまいます。

割込みドリブンならば割込みが発生したと同時に処理を開始できるため、応答性は原理的に向上するわけです。


割込みに入って戻るまでの内容を以下に示します。

①割込み発生。
②CPUが割込みを通知される。
③CPUはプログラム・カウンタとモード・レジスタの値をアトミックにスタックに格納(退避)する。(CPUの回路が複雑になるのを避けるためにたいてい一部のレジスタに限られる)
④CPUは割込み要因に応じて割込みベクタに設定してあるアドレスをプログラム・カウンタに強制的に書き換える。
⑤CPUはモード・レジスタを割込み無効に書き換える。
⑥割込みハンドラで汎用レジスタをスタックに格納(退避)する。
⑦割込みハンドラで引数の準備をするためにレジスタを変更する。
⑧割込みハンドラで関数を呼び出し、割込み要因のクリアを実行しつつ割込み処理を実施する。
⑨割込みハンドラで⑥で格納したスタックの値を汎用レジスタに値を格納(戻し)する。
⑩割込みハンドラでrteを実行し、③でスタックに格納した値をプログラム・カウンタとモード・レジスタにアトミックに格納(戻し)する。



ここでのポイントは割込みハンドラの記述である。
アセンブラで書く必要がります。

#include "intr.h"
.h8300h
.section .text

.global _intr_softerr
# .type _intr_softerr,@function
_intr_softerr:
mov.l er6,@-er7
mov.l er5,@-er7
mov.l er4,@-er7
mov.l er3,@-er7
mov.l er2,@-er7
mov.l er1,@-er7
mov.l er0,@-er7
mov.l er7,er1
mov.w #SOFTVEC_TYPE_SOFTERR,r0
jsr @_interrupt
mov.l @er7+,er0
mov.l @er7+,er1
mov.l @er7+,er2
mov.l @er7+,er3
mov.l @er7+,er4
mov.l @er7+,er5
mov.l @er7+,er6
rte

.global _intr_syscall
# .type _intr_syscall,@function
_intr_syscall:
mov.l er6,@-er7
mov.l er5,@-er7
mov.l er4,@-er7
mov.l er3,@-er7
mov.l er2,@-er7
mov.l er1,@-er7
mov.l er0,@-er7
mov.l er7,er1
mov.w #SOFTVEC_TYPE_SYSCALL,r0
jsr @_interrupt
mov.l @er7+,er0
mov.l @er7+,er1
mov.l @er7+,er2
mov.l @er7+,er3
mov.l @er7+,er4
mov.l @er7+,er5
mov.l @er7+,er6
rte

.global _intr_serintr
# .type _intr_serintr,@function
_intr_serintr:
mov.l er6,@-er7
mov.l er5,@-er7
mov.l er4,@-er7
mov.l er3,@-er7
mov.l er2,@-er7
mov.l er1,@-er7
mov.l er0,@-er7
mov.l er7,er1
mov.w #SOFTVEC_TYPE_SERINTR,r0
jsr @_interrupt
mov.l @er7+,er0
mov.l @er7+,er1
mov.l @er7+,er2
mov.l @er7+,er3
mov.l @er7+,er4
mov.l @er7+,er5
mov.l @er7+,er6
rte