公開日:2021/04/24
15th Season
#15-9 割り込み機能を追加する
マイコンを完成させることが目標だったシーズン15も残りあと2回です。前回、マイコンとして形が整ったので、「完結」と、思われましたか?やっぱり、割り込み機能のないマイコンって、どうなんでしょうか?マイコンとは、外部回路を制御するための装置ですから、何らかのイベントを割り込みとして受け取り、通常処理に割り込んで別な処理をさせたいケースがあるはずです。
残り2回でオリジナル16ビットCPUコアMoscoviumに割り込み機能を実装して、完結させたいと思います。
まずはCPU
最初に、オリジナル16ビットCPUコアMoscoviumに割り込み機能を追加しなければなりません。
インターフェイスとしてはCPU Moscoviumの最上位に割り込み要求信号irqを追加します。irqがアサートされると、CPUが割り込み処理を開始します。と、ここで、CPU様のキャラクタ設定を確認しておきましょう。
CPU様のキャラクタ設定:
- マイコンの中ではCPU様が一番偉いの👸。
- だからCPU様は、誰にも何も強制されないし、誰も何も強制できない。
- 周りの従者(I/Oユニット)はCPU様の仰せの通りに。
(1^^;)……な・の・で、irq信号で割り込み要求は受け取るけど、その割り込みを受け付ける(実行する)かどうかは、CPUが決めます。
従って、状態レジスタsrに割り込み許可IEビットを設けて、リセット解除後の初期値をIE=0(割り込み禁止)とします。CPUはリセット解除後、プログラムの初期化を実行し、割り込みを受け付けられる状態になったら、SrレジスタのIE=1として割り込みを許可します。
割り込み処理
そもそも割り込みと言うのは、CPUが通常の処理を実行中に、緊急度が高かったり、タイムリに処理すべきイベントが発生した時、一時的に通常処理に割り込んで別な処理を実行する事を指します。
従って、割り込みサービスルーチンの実行が終了したら、何事も無かったかの如く元の通常処理に戻らなければなりません。このためには最低限、割り込み処理に入る直前の状態レジスタsrと、プログラムカウンタpcの値を保存して、割り込みサービスルーチンから戻る時に復帰できるようにしておく必要があります。
CPUは割り込みを受け付けると、以下の様な割り込み処理(手順)を実行します。
- その時に実行中だった命令の終了を待つ。
- 命令終了時点のプログラムカウンタpcをスタックに退避する。
- 命令終了時点の状態レジスタsrをスタックに退避する。
- SrレジスタのIEビットを0にクリアして、多重に割り込みがかかることを抑止する。
- 割り込みベクタiv(新設)をPcに転送し、Ivが指し示すアドレス(割り込みサービスルーチン)から命令をフェッチして実行する。
ATmega328等のマイコンでは、「割り込みベクタテーブル」を持っていることが多いです。ATmega328の割り込みベクタテーブルは、プログラム空間のアドレス0x0000〜の数10バイトです。データシートなどで確認して見てください。このような方式をベクタ割り込み方式と言います。
この方式は、例えば、「タイマ1割り込み」「UART受信割り込み」「UART送信終了割り込み」など、割り込み要因毎に異なるベクタを指定できるので、あるベクタに分岐してきた時には、どの割り込み要因が発生したのか、自明なのです。この仕組みでは、割り込みベクタの先頭命令からすぐに処理を開始することが出来、タイムリに処理できることが特長です。
一方、リセット解除後、アドレス0x0000番地からプログラムを実行するとしたMoscoviumの仕様では、割り込みベクタテーブルを設置する余地がありません。そこで、割り込みベクタivを新設して、割り込みが起きた時の割り込みサービスルーチン先頭アドレスをユーザに指定してもらう仕様としました。
この方式では、全ての割り込み要因が同じIvレジスタアドレスに分岐するため、割り込みサービスルーチンの最初の処理は、「どの割り込み要因で割り込みがかかったのか?」を知ることになります。割り込み要因を確認する方法については、後ほど割り込みコントローラINTCの設計をする時に検討します。
実装
具体的には以下の実装が必要となります。
- レジスタファイルRGFブロックに割り込みベクタivを追加します。
- movtc命令やldch命令など、制御レジスタRcをオペランドに持つ命令にも、Ivレジスタを追加して制御できるようにする必要があります。
- 制御CTLブロックのステートマシンに、割り込み要求信号irqを受け付けて割り込み処理を実行するステートを追加します。
- 命令を追加します。
- 割り込み機能対応で、いくつか命令を追加します。
- rti命令:
- 割り込みサービスルーチンから戻る命令です。
- 割り込み開始処理でスタックに退避したSrとPcを回復して、元の処理に戻ります。
- sesrl命令,sesrh命令:
- Srレジスタの指定したビットに1をセットする命令です。
- 割り込み許可IEビットは、Srレジスタの第3ビット(下位バイト)なので、「sesrl 3」とすることで、割り込みを許可することができます。
- 「sesrl 3」は、組み込みマクロ命令「seie」を使っても同じ動作となります。
- 「ldcl sr,0x08」等としてもIE=1にできますが、ldcl命令では演算フラグ(N,C,V,Z)やバンク選択フィールド{BK1,BK0}が破壊されてしまうので、IEビットだけを操作できる命令があった方が便利なはずです。
- clsrl命令,clsrh命令:
- Srレジスタの指定したビットを0にクリアする命令です。
- 割り込み許可IEビットは、Srレジスタの第3ビット(下位バイト)なので、「clsrl 3」とすることで、割り込みを禁止することができます。
- 「clsrl 3」は、組み込みマクロ命令「clie」を使っても同じ動作となります。
- 「ldcl sr,0x00」等としてもIE=0にできますが、ldcl命令では演算フラグ(N,C,V,Z)が破壊されてしまうので、IEビットだけを操作できる命令があった方が便利なはずです。
- pause命令:
- 割り込み要求信号irqがアサートされるまでCPUを一時停止する命令です。
- 割り込みが発生するまですることが無い時などに使います。
ついでの命令拡張
ソフトウェア(OS等含む)の処理規定次第なのですが、割り込み処理に汎用レジスタバンクを予約すると、割り込みサービスルーチン内の処理を大幅に簡略化・高速化することが出来ます。
割り込みサービスルーチンは、元の通常処理に割り込んで実行されるのですが、割り込み処理終了後は、元の通常処理に戻らなくてはなりません。割り込み開始処理でスタックに退避するレジスタは、SrとPcだけなので、それ以外の汎用レジスタなど、割り込みサービスルーチン内で使用するレジスタは全て、プログラムでスタック等の外部メモリに退避・回復する必要があります。
例)
int_vect:
pushwm r0,r1,r2,r3 // 退避
// この先、汎用レジスタr0〜r3を使用可能
:
popwm r3,r2,r1,r0 // 回復
// 最初に退避したレジスタを回復してから戻る。
rti
予めプログラムを組む時に、「割り込みサービスルーチン内ではバンク3を使うので、通常処理でのバンク3使用は禁止」と、取り決めしておくと、上の例は下の様に簡単になります。
例)
int_vect:
ldcl sr,0x03 // バンク3を選択
// この先、全汎用レジスタr0〜r7を使用可能
:
// rti命令がSrレジスタを回復するので、自動的に元のバンクに戻る。
rti
汎用レジスタバンク機能によって、安いコストで8個の汎用レジスタをフリーに使用可能となりました。それでも、たったの8個なんですよ。割り込みサービスルーチン内の処理が複雑になって来ると、汎用レジスタ8個なんてすぐに使い果たして、足りなくなってしまうでしょう。
C言語のローカル(auto)変数みたいなデータ領域が欲しくなります。通常、こうした領域はスタック上に確保されます。すると、スタックポインタspからの相対アドレスで外部メモリにアクセスできる命令が欲しくなります。そこで、スタックポインタ相対間接アドレッシング命令、ldwsp命令とstwsp命令を追加しておこうと思います。
例)
int_vect:
ldcl sr,0x03 // バンク3を選択
// この先、全汎用レジスタr0〜r7を使用可能
:
movfc r0,sp // スタック上に4ワード(8バイト)のデータ領域を確保
subi r0,8
movtc sp,r0
// この先、[sp+0]〜[sp+6]までの4ワード(8バイト)を使用可能
:
ldwi r0,0x1234
ldbiu r1,4
stwsp [sp+r1],r0
ldwsp r2,[sp+r1]
:
movfc r0,sp // スタック上に確保したデータ領域を開放
addi r0,8
movtc sp,r0
rti
ただ、この命令を実行するためには、レジスタが1個足りません。下のCPUブロック図を眺めてみても、相対アドレスSp+Rnを計算して、MEMブロックからアドレスとして出力するパスが無いのです。
Sp+Rnの演算はALUを通すしかないので、Sp+Rnを演算した結果を一時的に保管するレジスタが必要となりました。そこで、制御レジスタに一時レジスタtrを追加することにしました。以下、新しいプログラマモデルです。
RGFブロックのブロック図も更新しておきました(下図)。
当面、ldwsp命令とstwsp命令でしか使わない一時レジスタtrを、CPUリソースにしてしまってはもったいないので、ユーザリソースとします。つまり、Trレジスタはユーザが自由に使用して良いです。ただし、特定の命令を使うと、Trレジスタの内容は破壊されてしまうことに注意を払う事が条件です。
※割り込みサービスルーチン内でTrレジスタを使う場合、Trレジスタをスタックなどに退避・回復することが必要となります。
以下の様に、バンク間で特定のレジスタ値を転送したい場合等にも使用可能です。
例)
ldcl sr,0x01 // バンク1を選択
movtc tr,r1
ldcl sr,0x02 // バンク2を選択
movfc r1,tr
一時レジスタtrを経由して、汎用レジスタr1の値をバンク1からバンク2へ転送出来ました。
割り込みコントローラ
前節で検討した通り、Moscoviumはベクタ割り込み方式ではないので、割り込みサービスルーチンの先頭で実施すべき処理は、「どの割り込み要因で割り込みが起きたか」を知ることです。これを知るために、割り込みを起こす可能性のあるI/Oユニットのレジスタを一々見に行って、割り込み要因を確かめるのは骨が折れます。
それに、I/Oユニットは必要に応じて増・減があるのに対して、CPUの割り込み要求信号入力はirq信号1本だけですので、各I/Oユニットからの割り込み要求を束ねて、1本の割り込み要求信号にしてCPUへ送信する役目を持った制御回路が必要です。
これを割り込みコントローラINTCにやらせようと思います。
INTCは以下の役割を担います。
- 全I/Oユニットからの割り込み要求を受けて、CPUに割り込み要求信号irqをアサートする。
- 必要に応じて、特定I/Oユニットからの割り込み要求を一時的にマスクする、割り込みマスクレジスタintmskを備える。
- 全I/Oユニットからの割り込み要求信号を一元管理して、その時点で割り込み要求のある割り込み要因を参照可能な割り込み要因レジスタintfctを持つ。
- 全I/Oユニットからの割り込み要因を、1から始まる割り込み番号として管理し、その時点で割り込み要求のある割り込み番号を読み出すことが出来る割り込み番号レジスタintnumを提供する。
- その時点で、複数の割り込み要求があった場合、intnumレジスタから読み出される値は、割り込み番号が一番小さい割り込みとなる。
- ユーザが分岐テーブルを作成し、intnumレジスタの読み出し値を使ってテーブルを引き、分岐すると、比較的迅速に割り込み要因を特定可能な工夫。
- 外部端子の状態を監視して、外部端子の状態に応じて外部端子割り込みを要求する。
- 外部端子割り込み制御レジスタintextを有する。
- 外部端子割り込みは2本(intc_int0,intc_int1)。
- トリガはLow Level/Falling Edge/Rising Edge/Both Edgeから選択できる。
- トリガが発生したことを示すフラグビット:EIFx
- CPUに対して割り込み要求を発行する許可ビット:EIEx
- 割り込み要求信号EIRx=EIFx&EIEx。
- ここで、xは、外部割り込み端子(intc_int0,intc_int1)のインデックスx=0または1。
- 現時点での、I/Oユニットからの割り込み要因数:最大16個、外部端子割り込み数:2個より、モジュール名をINTC162とする。
- 従って、intfctレジスタおよびintmskレジスタは16ビット幅。
- intnumレジスタは16ビット幅で、その時点で割り込み要求が無かった時には0が読み出される。
- 割り込み処理ルーチンの分岐アドレステーブルを作成しておくと、分岐アドレスを容易に読み出せるような仕組みを持つ。
- 1)intnumから読み出される値は、割り込み番号×2倍の値とする。
- 2)intnumに対するオフセット値をintofsレジスタで設定可能とする。
INTCのI/Oレジスタ仕様
INTCのI/Oレジスタ一覧を以下に示します。
Addr |
Name |
R/W |
Bit:15〜0 |
Memo |
0xf060 |
intnum |
R |
INUM[15:0] |
interrupt number |
intofs |
W |
OFST[15:0] |
offset |
0xf062 |
intext |
RW |
− |
− |
− |
− |
− |
− |
− |
− |
EI
F1
|
EI
F0
|
EI
E1
|
EI
E0
|
EIT1
[1:0]
|
EIT0
[1:0]
|
external interrupt |
0xf064 |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
reserved |
0xf066 |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
reserved |
0xf068 |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
reserved |
0xf06a |
intfct |
R |
OV
FR
1
|
CM
AR
1
|
CM
BR
1
|
− |
OV
FR
0
|
CM
AR
0
|
CM
BR
0
|
− |
− |
− |
− |
− |
EI
R1
|
EI
R0
|
− |
− |
interrupt factor |
0xf06c |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
reserved |
0xf06e |
intmsk |
RW |
OV
FM
1
|
CM
AM
1
|
CM
BM
1
|
− |
OV
FM
0
|
CM
AM
0
|
CM
BM
0
|
− |
− |
− |
− |
− |
EI
M1
|
EI
M0
|
− |
− |
interrupt mask |
BUSC出力のセレクト信号はbcs_intc_nです。上表の範囲でアサートされるので、INTCユニット内では、アドレスbadr[3:0]を復号してレジスタを特定しています。このI/Oユニットはバイトアクセス禁止です。必ずワードアクセスしてください。
- intnum: 割り込み番号レジスタ
- 下位6ビットから、現在割り込み要求の出ている割り込み要因の中で、一番小さな割り込み番号×2が読み出されます。
- 読み出し専用です。同じアドレスにintofsレジスタが割り当てられているので、書き込むとintofsレジスタへの書き込みとなります。
- 割り込み番号:
- 1,2=未定義
- 3=外部端子割り込み0(EIR0)
- 4=外部端子割り込み1(EIR1)
- 5〜9=未定義
- 10=タイマ0コンペアマッチB割り込み(CMBR0)
- 11=タイマ0コンペアマッチA割り込み(CMAR0)
- 12=タイマ0オーバフロウ割り込み(OVFR0)
- 13=未定義
- 14=タイマ1コンペアマッチB割り込み(CMBR1)
- 15=タイマ1コンペアマッチA割り込み(CMAR1)
- 16=タイマ1オーバフロウ割り込み(OVFR1)
- ※intfctレジスタに割り当てられた割り込み要因のビット番号+1と一致させています。
- intnumレジスタから読み出される値は、intnum=intofs+2×<割り込み番号>となります。
- <割り込み番号>は、その時点で割り込み要因がアサートされている割り込み番号の中で、一番小さい番号です。
- その時点で割り込み要求が無かった場合は、<割り込み番号>=0です。
- intofs: オフセットレジスタ
- 初期値: 0x0000
- 書き込み専用です。同じアドレスにintnumレジスタが割り当てられているので、読み出すとintnumレジスタからの読み出しとなります。
- intnumレジスタを読み出す際の、割り込み番号へのオフセット値を設定するレジスタです。詳しくは、intnumレジスタの説明を参照してください。
- intext: 外部端子割り込み制御レジスタ
- 初期値: 0x0000 (フラグ値は除く)
- 外部端子(intc_int0,intc_int1)からの割り込み要求を制御します。
- EIFx: 外部端子割り込みフラグ
- トリガフィールドEITxで指定されたトリガ条件が成立すると、1が読み出されます。
- 本ビットへの1書き込みでフラグはクリアされます。フラグをクリアしたくない場合には、0を書き込んでください。
- フラグが自動的にクリアされることはありません。
- EIEx: 外部端子割り込み許可ビット
- EIFx=1の状態で、EIEx=1となると、CPUに対して割り込み要因EIRxをアサートします。
- EIRx=EIEx&EIFxです。
- EITx[1:0]: 外部端子割り込みトリガフィールド
- EITx=2'b00: Low Level
- EITx=2'b01: Falling Edge
- EITx=2'b10: Rising Edge
- EITx=2'b11: Both Edge
- intfct: 割り込み要因レジスタ
- 各I/Oユニットからの割り込み要求が一元管理されているレジスタです。
- 割り込み要求のアサートされた要因ビットから1が読み出されます。
- このレジスタから読み出されるビット値には、intmskレジスタによる割り込みマスクは反映されていません。
- intmsk: 割り込みマスクレジスタ
- 初期値: 0x0000
- intmskレジスタの各ビットは、intfctレジスタの各ビットと対応しています。
- このレジスタに1が書き込まれたビット位置に対応する割り込み要因からの割り込み要求は、マスクされてCPUへの割り込み要求から除外されます。
- 初期値は0x0000ですので、本レジスタを設定しない限り、全ての割り込み要求はCPUに対して割り込み要求をアサートします。
- I/Oユニットの設定を変更せずに、特定の割り込み要因だけを優先して受け付けたい場合などに使用可能です。
- CPUへの割り込み要求信号irq=( (intfct&(~intmsk))!=16'h0 )です。
割り込み受付までの道のり
I/Oユニットで発生した割り込み要求が、CPUで受け付けられるまでの道のりを下図に示します。
- I/Oユニット
- I/Oユニットで割り込み要因となるイベントが発生すると、フラグビットxxxFに1が立ちます。
- この時、対応する割り込み許可ビットxxxEに1が設定されていると、割り込み要求信号xxxRがアサートされます。xxxR=xxxE&xxxFです。
- INTC
- I/Oユニットから割り込み要求信号xxxRを受け取ると、それを割り込み要因レジスタintfctに反映します。
- 割り込みマスクレジスタintmskの対応するマスクビットxxxMが0にクリアされた割り込みがあると、CPUに対して割り込み要求信号irqをアサートします。マスクビットxxxM=1の要求はマスクされます。
- irq=( intfct&(~intmsk)!=16'h0000 )です。
- FF
- INTCの出力で1段FFが入っています。
- 従って、I/Oユニットの割り込みフラグがCPUへの割り込み要求信号irqに伝わるまで、1サイクルのタイムラグがあります。
- CPU
- irq信号がアサートされた時、状態レジスタsrの割り込み許可IEビット=1ならば、割り込みを受け付けて、割り込み処理を開始します。
ATmega328等のマイコンに比べて、1段(INTCのマスクレジスタ分)多くなっています。
あまり、設定を複雑にするのは好みではないのですが、ここでは敢えて、INTCのマスクレジスタintmskを追加して3段にしてあります。INTCのintmskレジスタは初期値=許可で、何も操作しなければ設定不要なので、許してください。I/Oユニットのレジスタには触らずに、一時的に特定の割り込みをマスクする仕組みとして追加しました。
そのような用途で使わないなら、intmskレジスタを設定しなければ、無いのと同じです。
INTCブロック図
INTCのブロック図を以下に示しておきます。
筆者の場合、コーディングする前にブロック図は描きますね。どういう機能ブロックに分割して、どのブロックにどんな機能を持たせるかをイメージしながら、基本設計を進めます。ただ、コーディングしている途中で、「ダメだな」とか思い、ブロック図に戻って修正して出直すことはしばしばです。
割り込みサービスルーチン考察
割り込み受付後、割り込みサービスルーチンでのプログラムの想定動作フロウを検討しておきます(↓)。
- 0)CPUに対して割り込み信号irqがアサートされ、IEビット=1だった時、CPUはプログラムカウンタpc→状態レジスタsrの順でスタックに退避する。
- その後、IEビット=0にクリアしてから割り込みベクタivの指し示すアドレスに分岐する。
- ここから先は割り込みサービスルーチン(ISR)内のソフトウェア処理となる。
- 1)ISR内で使用するレジスタをpushw命令やpushc命令を使ってスタックに退避する。
- 場合によっては、Srレジスタのバンクフィールドを変更して、予め取り決めて割り込み処理用に引き当てたバンクに変更する。
- 2)intnumレジスタを読み出して、割り込み要因を確かめる。
- intnumから読み出されるのは、割り込み番号(1〜16)×2なので、これを元に分岐テーブルを引くと、各要因毎に処理すべきISRアドレスへ分岐可能。
- あらかじめintofsレジスタに分岐テーブルの先頭アドレスを書き込んでおくことにより、分岐処理を高速化できる。
- 複数要因で割り込み要求があった場合、intnumからは、より小さい割り込み番号が読み出される。
-
例)
// 初期化: intofsレジスタにisr_tableアドレスを
// オフセット値として書き込んでおく
ldwi r7,intofs // intofs=isr_tableに設定
ldwi r0,lab_isr_table
stw [r7],r0
:
:
// ISR
isr_vect:
// 割り込み番号intnumを使って、対応するISRアドレスへ分岐
pushw r0
ldwi r0,intnum
ldw r0,[r0] // intnum=intofs+<割り込み番号>×2を読み出し
ldw r0,[r0] // isr_tableから分岐アドレスを読み出し
jmp r0 // 読み出した分岐アドレスへ分岐
// 分岐テーブル
isr_table:
datw lab_isr_num0
datw lab_isr_num1
:
datw lab_isr_num15
datw lab_isr_num16
isr_num0:
popw r0
rti
- 上のコード例がそのまま、ベクタ割り込み方式を採用しなかった分のオーバヘッド(分岐までおよそ9サイクルほど)。こうして見ると結構大きい……。やはり、マイコンにはベクタ割り込み方式の方が適している様だ。
- ラベルisr_num0は、割り込み要求が無かった場合に分岐するアドレスなので、rti命令のアドレスを設定しておく。
- 2’)intfctレジスタを読み出して、割り込み要因を確かめる。
- intnumレジスタでは、固定された割り込み番号の若い順と言う優先順位に従って読み出されてしまう。
- 固定された優先順位と異なる優先順位で処理する必要がある場合には、intfctレジスタを参照して、ソフトウェアの期待する優先順位に従って、割り込み処理を実行する。
- intfctレジスタには、全てのI/Oユニットからの割り込み要因が集められている。
- intfctレジスタにビットの立った割り込み要因の中で、処理優先順位の高い割り込みから順に処理する。
- ただしintfctレジスタには、intmskレジスタの設定値が反映されないことに注意。
- 必要に応じて、intfct&(~intmsk)の演算を実行。
- 3)処理が終わったら、対応するI/Oユニットの割り込みフラグをクリアする。
- 割り込み要因フラグは自動クリアされないので、明示的にフラグをクリアしないと、rti命令実行後、再び同じ要因で割り込みがかかってしまう。
- 4)1)で退避したレジスタをpopw命令やpopc命令を使って回復する。
- バンクフィールドは、rti命令がSrレジスタを回復した時に元に戻るので、処理不要。
- 5)rti命令で割り込み処理を終了し、通常処理へ復帰する。
- ただし、この時点で他の割り込み要求がアサートされていた場合、rti命令実行直後、すぐに次の割り込みがかかってしまう。
- 5’)intnumレジスタを読み出してintnum≠intofs+0だった場合、まだ別な割り込み要因が残っているので、以下の様にして割り込みベクタivに分岐する。
-
movfc r7,iv
jmp r7
- 他の割り込み要因が発生していなかった場合、intnum=intofs+0となって読み出されるので、そのままrtiして良い。
- 一旦rti命令で戻って、再度割り込み要求を受け付けることで、Sr,Pcの退避・回復が実行される。その分のオーバヘッドを嫌う場合の処理。
- そういう意味では1)と4)における、レジスタの退避・回復も不必要に実行されてしまうので、単純にIvアドレスへ戻るのではなくて、2)の処理アドレスへ分岐させた方が良い。
- その場合、5’)の処理(手順)は、手順4)の実行前に移動する必要がある。
手順2)と2’)、5)と5’)は、どちらか一方必要な方を選んで実行すればOKです。特に優先順位を気にせず、割り込み要因を1個づつ処理して行けば良い場合には、2’)ではなくて2)を実行すれば良いし、多少のオーバヘッドを許容する場合は、5’)の代わりにそのまま5)を実行してしまえば良いです。
次回、シーズン15の最後に、CPUに対して割り込みを要求するI/Oユニットとして16ビットタイマを設計してこのシーズンを閉じようと思います。