「天文屋のためのマイコン入門」割込み最終説明
今日は少し間が空きましたが、割り込みについて最後の説明をします。割り込みは一種のマルチタスクですが、マルチタスクには必ず「資源の競合」という問題がおきます。
今、通常のメイン処理で、PORTAレジスタに値10を入れるため、ワーキングレジスタに10を代入する命令と、それをPORTAに代入する2つの命令があるとします。
この2つの命令の間に、割り込みが入り、割込み処理でワーキングレジスタの値が書き換わったとします。この場合、PORTAには10の値は入らず、何か違う値になっています。これはまずいですね。このような不都合を資源の競合といいます。
同じようなことがゼロフラグでも起こります。レジスタをデクリメントしてそれが0かどうかでスキップするか否かの命令があるとします。ちょうどこの瞬間に割り込みが入り、フラグが書き換わったら(ほとんどの命令はフラグを書き換える)、元のメイン処理に戻っても正常な処理はできないでしょう。
また同じようなことがバンク番号や他の特殊なレジスタでも起こります。
このような不都合を避けるには、メイン処理と割込み処理の両方でワーキングレジスタまたはフラグを使わないことです。ただそれは無理です。ワーキングレジスタを使わなければ、できない処理もありますし、ほとんどの命令はフラグの値を変化させます。
ちなみに前回出したタイマー割込みの例ではメイン処理で何もしていなかったので、正常にプログラムが動作したのでした。
ではどうすれば良いか。
その一つ解決方法が、割込み処理の先頭で、ワーキングレジスタとフラグ(つまりSTATUSレジスタ)の値をどこかに保存しておいて、割り込み処理の最後に元の値に戻してやります。
具体的な方法はこうです。
まず、ワーキングレジスタとSTATUSレジスタの保存先を、126番地と127番地の汎用レジスタとします。そこでシンボルを次のように定義します。
W_BUF EQU 126
STATUS_BUF EQU 127
そして、割込み処理の先頭でワーキングレジスタとSTATUSレジスタをW_BUFとSTATUS_BUFに転送しなければならないのですが、これが実はやっかいなのです。
ORG 4
W -> W_BUF
STATUS -> STATUS_BUF
なぜなら、その過程でワーキングレジスタとSTATUSレジスタの値を変化させてはいけないからです。特にSTATUSレジスタの方がやっかいです。というのはほとんどの命令がフラグを変化させるからです。つまりSTATUSレジスタが変化する。
その命令がフラグを変化させるかどうかは、PICのデータシートの命令表のところを見ます。
この表の赤枠の部分が変化するフラグです。ここは空白の命令だけ使えます。
たとえば割込み処理の先頭で次のような命令を書いたらどうでしょう?
ORG 4
MOVWF W_BUF ;1 W -> W_BUF ワーキングレジスタ保存
MOVF STATUS, W ;2 STATUS -> W STATUSレジスタをWに転送
MOVWF STAUS_BUF ;3 W -> STATUS_BUF それを保存
1と3のMOVWF命令はフラグを変化させないので問題ありません、ところが2のMOVF命令は問題です。この命令はその結果によって、たとえば転送した値が0かどうかによってフラグが変化します。つまりSTATUSレジスタの値が変わってしまいます。
つまり、STATUSレジスタの値を保存したいのに、その保存する命令によってもともとの値が変わってしまうのです。これはすごい自己矛盾です。
実は、STATUSレジスタのフラグが変化するタイミングが転送後なら、何にも問題なかったのです。転送する前に、その値によってSTATUSレジスタのフラグの値が変わってしまいます。変わった後のSTATUSレジスタの値を保存したって、何の役にもたちません。メイン処理には、STATUSレジスタを元の状態に戻して返してやる必要があるのです。
それでは、この問題をどうやって解決するか次回、説明します。
| 固定リンク
コメント