C言語でコードを記述している際にCPUの割り込みを停止したい事がある。
割り込み機能を停止する際はCPU命令のDI/EIを使う。
sdccではインラインアセンブラが可能なので、割り込みを停止したいところでは
DI/EIなどをインラインして部分的に割り込みを停止させる。
//INLINE
__asm
di
__endasm;
//.......
//INLINE
__asm
ei
__endasm;
このインラインを用いる方法以外に、関数自体をクリティカル指定
する方法もある。
これらはsdccの拡張で、関数名に修飾子__CRITICALを追加する。
void function(void) __critical {
//Interrupt critical section
}
クリティカル指定された関数は、関数の実行時には必ず割り込みが禁止され、
終了時に割り込みが許可される。
クリティカル指定された関数は、実行されるタイミングで必ず割り込みが停止するので、
割り込みを利用した擬似スレッド処理なども必ず停止する。
そうした意味ではスレッド間のロック問題を扱うクリティカルセクションに
似ている。
sdccではこの他に__interrupt修飾がある。
__interruptは__criticalと組で用いる。
void function(void) __critical __interrupt {
//Interrupt INT38h code
}
もしこのような関数名のコードを作成すると、関数のスタート時に
割り込みを停止し、同時に、全レジスタをスタックへ自動的に退避する。
処理が終わるとレジスタのデータを元に戻し、割り込み処理を再開する。
この一連の機能は割り込みを前提とした関数を作成する場合に必要となるものだ。
割り込みの停止はインラインアセンブラで書いて代替することも可能だが、
sdccでは関数名を修飾することでより判りやすいCコードを記述する事が出来るようになっている。
割り込み処理は、通常アセンブラで書くことが多いが、
関数名に__critical __interruptを指定し、割り込みベクタージャンプテーブル
を書き換えれば、C言語の関数で割り込み処理を記述することが可能だ。
下手にアセンブラで割り込み処理を書く必要は無い。
割り込みベクトルをBIOS(DOS)から自作関数へ書き換える場合はメモリーの書き換えを行なう。
特定のメモリーアドレスを書き換える場合はポインタアクセスとなるだろう。
例えばINT38hを書き換える場合は、該当箇所はJP 0xxxxhとなり3byteなので
アドレスだけ変更する場合はアドレスの0x39,0x40を書き換える。
割り込み処理関数をInterrputVector38h()であると仮定すると以下のようになる。
//ポインタ型を定義
unsigned int *vector;
//INT38hをセット
vector=(unsigned int *)0x0039;
//関数へのポインタを渡す
*vector=(unsigned int)InterruptVector38h;
これでINT38hの割り込みベクターエントリアドレスがInterruptVecto38h()
への関数へのポインタとなった。
これで
システムタイマーの1/60Secの割り込みは、BIOSを経由せず、
必ず自前の関数InterruptVector38h()が呼ばれる。
ここにシステムファームウエアルーチンの全てを組み込めばBIOSは必要ではない。
割り込み処理をアセンブラではなくCで書くことも出来る。
元に戻す際は、DOS動作時のINT38hオリジナルの値に書き戻す。
//INT38h RECOVER
*vector=(unsigned int)0xDD97;
最終更新:2012年04月15日 02:00