VCPI メモ 第2回: プログラムを幾らかマシにする
2024-07-27
タグ: #asm #DOS第1回で取り敢えずプロテクトモードに行って V86 モードに戻って来ることができるようになりました。 vpmxfer が提供している MMIO の処理も第1回で紹介した範囲の内容で実現できます。
しかし、折角 VCPI の復習を始めたので、もう少し実装を改善したり、機能を追加したりできるところがあればやってみることにしました。 この第2回では第1回のサンプルプログラムを幾らか改良しようと思います。
サンプルコード
今回説明する改良を加えたサンプルプログラムのコードは vcpi_02.asm からダウンロードできます。 このコードも PDS とします。 アセンブル方法は第1回の説明を参照してください。
プログラムの内部構造は変わっていますが、基本的な挙動は第1回から変わっていないため、実行結果例は省略します。
改良する箇所
以下2箇所を改良します。
- 32bit コードを別のセグメントに分ける
- ページテーブルを確保する場所を変える
まず、第1回のサンプルコードでは、16bit のコードと32bit のコードを単一の D=0
なセグメントディスクリプタで指して実行していました。
しかし、D=0
即ち USE16
なセグメントに 32bit 用コードを配置すると、必要に応じて手動でプレフィックスを配置しなくてはならない場面があり不便です。
例としては、32bit アドレスにアクセスするストリング命令を使いたい場合が挙げられます。
この問題を解決するため、USE32
なコードセグメントを導入し、プロテクトモードで実行するプログラムはそのセグメントに配置するようにします。
次に、第1回のサンプルコードでは、ページテーブルをコンベンショナルメモリ内に配置していましたが、これを仮想 EMS を利用してプロテクトメモリに配置するようにします。 これにより、コンベンショナルメモリの消費量を減らすことができます。
32bit コード用セグメントを GDT に追加する
32bit コード用セグメントを追加した GDT の例を以下に示します。
用途 | limit | base | アクセスバイト | 備考 |
---|---|---|---|---|
NULL | 0 | 0 | 00h | |
TSS | TSS の大きさ | TSS 先頭 | 89h | |
16bit コード | 64KiB | CS 先頭 | 9ah | D=0 とする |
データ | 64KiB | DS 先頭 | 92h | |
スタック | 64KiB | SS 先頭 | 92h | |
32bit コード | 64KiB | 32bit コードセグメント先頭 | 9ah | D=1 とする |
V86 モード移行時用 | 第1回参照 | 0 | 92h | 移行時に DS に設定 |
VCPI コード | - | - | - | VCPI サーバが設定 |
VCPI データ0 | - | - | - | 同上 |
VCPI データ1 | - | - | - | 同上 |
32bit コードセグメントを導入しましたが、V86 モードでの命令解釈はリアルモードと同じですから、V86 モードで USE32
なセグメントの命令を実行してしまうと、命令列によっては想定していない挙動となることが予想されます。
これを避けるため、V86 モードに戻る処理を 32bit コードセグメント中に配置するとしても、戻り先の命令は 16bit コードセグメント中の命令とするのが良いです。
一方、プロテクトモードではセグメントの D
フラグを見てくれるので、GDT 等の設定が正しくできていれば、プロテクトモード移行直後に想定外の命令解釈が行われることはありません。
よって、以下どちらの方法で 32bit コードセグメントへ移動しても大丈夫です。
- プロテクトモード移行先の命令は 16bit コードセグメントに置き、そこから 32bit コードセグメントへ飛ぶ
- プロテクトモード移行先の命令を元から 32bit コードセグメントに置く
プロテクトメモリにページテーブルを配置する
VCPI には 0MiB~1MiB の範囲のアドレスにページングにより割り当てられているメモリの物理アドレスを取得する機能があります。
CX
に4KiB 単位のページ番号(リニアアドレスを 12bit 右シフトした値)を入れ、int 67h (AX=0de06h)
を呼び出します。
成功すると AX=0
が返り、この時 EDX
に CX
で指定した 4KiB ページの物理アドレスが入っています。
失敗すると AX
に0以外の値が返ります。
この機能を利用することで、以下の手順で EMS にページディレクトリとページテーブルを配置できます。
- EMS のメモリを確保し、EMS ページフレームにマップする。
- ページテーブルとして使うメモリ領域の物理アドレスを取得する。
- 手順2で得られた物理アドレスをページディレクトリとして使うメモリ領域に設定する。
- ページディレクトリとして使うメモリ領域の物理アドレスを取得する。
- 手順4で得られた物理アドレスを
CR3
への設定値とする。
サンプルプログラムでは、下記のように、最下位の EMS ページ冒頭の 4KiB をページディレクトリに、その次の 4KiB ~ 8KiB の領域をページテーブルに使っています。
+====================+ 16KiB
| 未使用 |
+--------------------+ 12KiB
| 未使用 |
+--------------------+ 8KiB
| ページテーブル |
+--------------------+ 4KiB
| ページディレクトリ |
+====================+ 0KiB
次回予告
ここまで(というか第1回で)調べ直した内容で vpmxfer で行っている処理は実装でき、復習の観点からは大体満足してしまった感じもするので、第3回以降があるかは現状微妙です。
現状のコードではプロテクトモードに移行した後も割込を禁止しており、プロテクトモードで時間のかかる処理を行えないという問題があります。 そこで、もし第3回があれば割込を取り扱うことになるだろうかとは思っています。