マルチタスクへの長い道程 その2 — つぎはぎOS

タイマー機能について誤解していたようだ。T-Kernelではシステムタイマーと物理タイマーとで、別になっていて、今まで作成したPITを利用したタイマー割込みはT-Kernel2.0/SMの物理タイマー機能に当たるようだ。
何が違うかと言うと、システムタイマーでは1985年1月1日(GMT)からのミリ秒とマイクロ秒を保持しなければならず、PC-ATではCMOSのRTC(Real-Time Clock)でシステム時刻を持ってカレンダー情報を提供している機能に当たるようだ。PITをそのままシステムタイマーとして使うことも考えたが、どちらにしても、時分年月日の情報を取得するためにRTCへアクセスするのだし、RTCはIRQ8の周期的な割込みを発生させることもでき、CMOSのRTCを使う方が便利だと思った。

という訳で、今までのPITでの割込み機能は物理タイマー機能のAPIへと修正することにした。その前に、改めてCMOSへのアクセスと、RTCからのIRQ8の割り込みハンドラを作成しなければならなかったが、もう慣れたもので、T_Kernel2.0仕様ではミリ秒とマイクロ秒単位のカウンターを持つようになっているが、手抜きでマイクロ秒単位のシステムコールは実装せず、RTCは10msec周期でIRQ8割り込みを発生させるように作成した。
RTCはなぜかCMOS(というRAM領域)へ年、月、日、曜日、時、分、秒までしか書き込んでくれない。しかも、通常のバイナリ数値ではなく、BCD表現でCMOSの内部RAMへ値を書き込んでいる。最初はそれに気づかず、バイナリだと思ってプログラミングして、どハマリした。英語のサイトでは、レジスタBを最初に読んで、モードを確認するよう記述があった。レジスタBの値を見て、BCD表現なのか、12時間表現なのか、米国サマータイムが有効なのかを判定するよう推奨していた。

Linuxでは、起動時にRTCの値を読むだけで、後はソフトウェアカウンターで加算しているらしい。私もRTCを読んでミリ秒へ変換し、IRQ8割り込みごとにカウントアップする方法を取ることにしていた。しかし、最初の一回だけIRQ8が発生するだけで、周期的に割込みが起こらなかった。サンプルプログラムも見ながら設定を確認したが、何をやっても駄目だった。
英語サイトによると、RTCからの割り込みはPIC回路との関連で相性が悪いらしく、割込みが”undefined state”となり、EOIができなくなるなどのトラブルがあるらしい。”IRQ danger”などという物騒な?短いチャプターで忠告までしていた。RTCは優先度が高いはずだが、IRQ8などというスレーブPICに接続されていることも関係しているのかもしれない。なぜ低い優先度の割り込みなのか理由は分からないが、PC-ATのつぎはぎの発展の歴史的経緯があるのかもしれない。
それで方針を変更し、RTCからは起動時に時刻を取得して初期値として保存し、PITのタイマーを使って10msec毎にカウントアップすることにした。PC-ATのPITで割込み使えるのはTimer0だけなので、結局、T-Kernel2.0/SMの物理タイマー機能は実装を止めることにした。

Linuxは1970年1月1日 00:00:00(UTC)からのミリ秒(設定によりマイクロ秒の高精度タイマーが使える)、Windowsは19701970年1月1日 00:00:00(ローカルタイム)からのミリ秒のカウント値持つらしいが、T-Kernel2.0では1985年1月1日(GMT)からのカウント値となっていた。理由は不明。拡張でマイクロ秒のカウンターも持つことになっている。困ったのは、「うるう年」と「うるう秒」の計算で、いろいろ調べて、うるう秒は諦め、うるう年の処理を適当に作成した。どうせ正確な時刻にはならないし、私個人では正確な検証はできない。将来インターネットに接続できるようになったら(いつのことやら)、NTPで時刻を補正する機能を実現する時に、また考えようと思う。グレゴリオ暦では、うるう年は以下のような条件を満たす年らしい。1985年以降で何回あるかをカウントして、日数を足すことにした。(2023年現在は9回。来年がうるう年)

・西暦が4で割り切れる年をうるう年とする (ユリウス暦と同じ)
・しかし、上記のうち、西暦が100で割り切れる年はうるう年としない
・だがしかし、上記のうち、西暦が400で割り切れる年はうるう年とする

うるう秒は、三年に一度、加算か減算するのだが、これは国際機関が原子時計との誤差を見ながら、いつ変更するかを決めているので、プログラムで計算することはできない。NTPで通知された時に変更するしか方法がない。

ミリ秒単位の時刻までは正確に検証できないので、2023/2/23という日付までの算出値が正しいかどうかを確認し、午後の15時までの時間だけのミリ秒を電卓の値と同じであることを確認した。うるう年も含めて、そこまでは何とか正しい値となっていた。
また、Timer-0によるカウントアップも行われ、T-Kernel2.0のtk_ref_tim()のシステムコールで、日時のミリ秒と合算した戻り値を返しているらしいことを確認した。(戻り値が正確なミリ秒の時刻かどうかは確認のしようがない)RTC回路への時刻補正は現時点では実装しなかったため、tk_set_tim()はTimer-0のソフトウェアカウンター値を訂正して、偽装した。GMTのままだと日本時間と9時間ずれるので、日本時間へのローカルタイムの変換は、NTPを実装する時に改めて実装し直そうと思う。

RTC/CMOSのIRQ8でのソフトウェアタイマーカウンターの実装は諦めたが、あるサイトの記事によると、RTCにも歴史的な発展の過程でいくつも種類があるらしく、CPU ID命令でTCSの種類を判定して、CPUの種類でRTCの種類を判定して処理を変えるらしい。サイト検索では、CPUの種類を調べるのはウィルスが使う基本テクニックらしく、それに関連したコンピューターウィルスの記事が多かった。面倒臭いので、深入りしないことにする。

カテゴリー: Linux, VirtualBox, つぎはぎOS パーマリンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です