DOS はリアルモードで実装する方法と、プロテクトモード(32bit)で
実装する方法の2通りが考えられます。
今回は後者、つまりカーネルをプロテクトモード(32bit)で実装して
アプリケーション実行時に仮想86モードに切り替える方法をとることにしました。
もともとDOSは16bitのリアルモードで動くOSです。なぜプロテクトモードに
切り替えるのかという疑問が出てくるでしょう。
以下、特に断らない限り、リアルモードを16bit(モード)、
プロテクトモードを32bit(モード)と呼ぶことにします。
カーネルを16bitモードで実装すると、
32bitモードで実装する場合に比べ、
既存のドライバなどとの互換性は高くなります。
例えば、既存のメモリマネージャ(HIMEMやEMM386など)をそのまま使うことができます。
しかし、すべてのコードが16bitモードで実行されますので、
16bit境界やメモリ消費量を意識しながら開発することになります。
一方、カーネルを32bitモードで実装すれば、
16bit境界やメモリ消費量の悩みはなくなり、
WindowsやLinuxと同じ感覚で
開発をすすめることができます。
メモリマネージャについては、同等の機能を提供すれば良いでしょう。
また、実行には386以上のCPUが必要になりますが、
PCのCPUのほとんどは386以上ですから、この制限は
あまり気にしなくても良いでしょう。
これが、今回32bitモードを選んだ理由です。
DOSアプリケーションは16bitのコードとして実行されます。
DOSの機能を提供するためには、カーネルの一部は16bitのコードとして
実装しておき、適宜32bitと切り替える必要があります。
そこで、どの機能を16bitで実装するか考えた結果、
次のようなコードは16bitで実装したほうが良いだろうという
結論になりました。
| 機能 | モード | 補足説明 |
| INT 20h〜2Fh | 32bit | DOS の機能を提供するソフトウェア割り込みです。 16bitのコードからフックされる可能性がありますので 16bitのエントリポイントを提供する必要があります。 |
| INT 31h | 32bit | INT 31hはDPMIを提供する割り込みです。 16bitのコードから呼び出されることはまずありません。 |
| INT 67h | 32bit | INT 67hはEMSやVCPIを提供する割り込みです。 16bitのコードから呼び出されますが、 32bitで実装しても問題ありません。 ただし16bit コードから far call されることがありますので、 16bitのエントリポイントとして INT 67h、IRET を用意します。 |
| その他のINT xxh | 16bit | DOS 用の割り込みベクタ(INT 20h〜INT 3Fh)を含め、ソフトウェア割り込みは 16bitのコードからフックされる可能性があります。 |
| キャラクタデバイス | 16bit |
念のため16bitで実装します。 CD-ROMドライバなど、一部のドライバは 16bitのコードから直接呼び出される可能性があるためです。 |
| ブロックデバイス | 32bit | おそらく、アプリケーションから呼び出される可能性は低いでしょう。 |
| OSの初期化コード | 両方 | 初期化コードはその一部を16bit、残りを32bitで 実装します。なるべく32bitを使うようにします。 |
| モード切り替えコード | 両方 | 32bitモードで実装するため、 16bitと32bitを切り替えるコードを実装する必要があります。 |
| 特権命令のエミュレーション | 32bit | DOSアプリケーションの多くは、本来OSが実行すべき ソフトウェア割り込みなどの特権命令を呼び出します。 アプリケーションがこれらの命令を実行すると、 CPUの設定によってはCPU例外の#GP(0)が発生します。 少なくとも CLI、STI、PUSHF、POPF、INT、IRET、IN、OUT、INSx、OUTSx、HLT については、命令をエミュレートするか、#GP(0)が 起こらないようにCPUを設定する必要があります。 なお、#GP(0)は一般保護違反などと呼ばれています。 |
| ステップ1 | 16bitから呼び出されます。 |
| ステップ2 | #GP(0)により32bitに切り替わります。 |
| ステップ3 | フック対策としてINTをエミュレートし、16bitモードに戻します。 |
| ステップ4 | フックされていなければ再度32bitに切り替えてシステムコールを実行します。 |
| ステップ5 | 16bitの呼び出し元に戻ります。 |