VCPI メモ 第2回: プログラムを幾らかマシにする

タグ: #asm 

更新履歴

  • 2025/08/12:VCPI の int 67h (AX=0de06h) の説明を修正

第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 の例を以下に示します。

用途limitbaseアクセスバイト備考
NULL0000h
TSSTSS の大きさTSS 先頭89h
16bit コード64KiBCS 先頭9ahD=0 とする
データ64KiBDS 先頭92h
スタック64KiBSS 先頭92h
32bit コード64KiB32bit コードセグメント先頭9ahD=1 とする
V86 モード移行時用第1回参照092h移行時に 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) を呼び出します。 成功すると AH=0 が返り、この時 EDX に CX で指定した 4KiB ページの物理アドレスが入っています。 失敗すると AH に0以外の値が返ります。

この機能を利用することで、以下の手順で EMS にページディレクトリとページテーブルを配置できます。

  1. EMS のメモリを確保し、EMS ページフレームにマップする。
  2. ページテーブルとして使うメモリ領域の物理アドレスを取得する。
  3. 手順2で得られた物理アドレスをページディレクトリとして使うメモリ領域に設定する。
  4. ページディレクトリとして使うメモリ領域の物理アドレスを取得する。
  5. 手順4で得られた物理アドレスを CR3 への設定値とする。

サンプルプログラムでは、下記のように、最下位の EMS ページ冒頭の 4KiB をページディレクトリに、その次の 4KiB ~ 8KiB の領域をページテーブルに使っています。

+====================+ 16KiB
| 未使用             |
+--------------------+ 12KiB
| 未使用             |
+--------------------+ 8KiB
| ページテーブル     |
+--------------------+ 4KiB
| ページディレクトリ |
+====================+ 0KiB

次回予告

ここまで(というか第1回で)調べ直した内容で vpmxfer で行っている処理は実装でき、復習の観点からは大体満足してしまった感じもするので、第3回以降があるかは現状微妙です。

現状のコードではプロテクトモードに移行した後も割込を禁止しており、プロテクトモードで時間のかかる処理を行えないという問題があります。 そこで、もし第3回があれば割込を取り扱うことになるだろうかとは思っています。


投稿一覧に戻る トップに戻る