
ようやく「メンドクセー」プロテクトモード(セグメンテーション)の復習を一通り完了。今回は実際にプログラムを動かしてみたいと思います。しかし「プロテクトモード」に入るということ自体、難行苦行。昔はサラサラ書いていた気がしないでもないのですが、忘却力の老人の気力が持ちませぬ。そこで御すがりするのがDOS4GWとな。
※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら
※実機動作確認(といってもエミュレータなんだけれども)には以下を使用させていただいております。
DOS/4GW
FreeDOSのインストールCDに漏れなく付属しておるWatcom Cに同梱されているDOS/4GWは、「DOSイクステンダ」と総称されるソフトウエアの1種です。1Mバイト(実際には640kバイト)の空間に閉じ込められているDOS上のソフトウエアに広大なメモリ空間を与えてくれるソフトウエアの1種です。DOS/4GWは、DOS4GのWatcom C同梱版(無償限定)という理解であります。知らんけど。
さて、以下の別件シリーズ過去回でDOS4GWで使える、VCPIだのDPMIだのよくわからんインタフェースどもをちょいと調べていたのは他でもありません。このソフトウエアに乗っかってプロテクトモードに忍びこむためであります。
トホホな疑問(62) FreeDOSのプログラムがプロテクトモードで走っているの確認して~
入ってしまえばコッチのもんだ、と。ホントか?
しかし、DOS/4GW使う注意点が一つ。386以降の32ビットCPU用の32ビット・プロテクトモードに入ってしまいます。本来的には286範囲の16ビット・プロテクトモードの復習のためだったけれども。多少、踏み出してしまっているところはゴメンクサイ。
今回のテストコード
そういう分けで、使用した処理系は過去回で使っていた「x86史上でも強力な」NASMアセンブラではなく、「灰の中から蘇った」WatcomCコンパイラとなります。肝心のアセンブラ部分はといえば、WatcomCのインライン形式を使ってます。こんな感じ。
#include <stdio.h>
void printSelector(const char* nam, short sel)
{
printf("%s %04X ", nam, (sel & 0xFFF)>>3);
if ((sel & 0x4) == 0) {
printf("GDT");
} else {
printf("LDT");
}
printf(" RPL=%d\n",(sel & 0x3));
}
void printAR(const char* nam, long ar)
{
printf("%s ", nam);
if ((ar & 0x800000)!=0) {
printf("4Kpg");
} else {
printf("Byte");
}
printf(" DPL=%d ",(ar & 0x6000)>>13);
switch ((ar & 0xe00)>>9) {
case 0:
printf("R/O(U)");
break;
case 1:
printf("R/W(U)");
break;
case 2:
printf("R/O(D)");
break;
case 3:
printf("R/W(D)");
break;
case 4:
printf("E/O ");
break;
case 5:
printf("E/R ");
break;
case 6:
printf("E/O(C)");
break;
default:
printf("E/R(C)");
break;
}
if ((ar & 0x100)!=0) {
printf(" ACCESS\n");
} else {
printf("\n");
}
}
void main()
{
short cs_selector, ss_selector, ds_selector;
long cs_ar, ss_ar, ds_ar;
_asm {
.386p
mov ax, cs
mov cs_selector, ax
lar eax, eax
mov cs_ar, eax
mov ax, ss
mov ss_selector, ax
lar eax, eax
mov ss_ar, eax
mov ax, ds
mov ds_selector, ax
lar eax, eax
mov ds_ar, eax
}
printSelector("CS: ", cs_selector);
printAR("CS_AR: ",cs_ar);
printSelector("SS: ", ss_selector);
printAR("SS_AR: ",ss_ar);
printSelector("DS: ", ds_selector);
printAR("DS_AR: ",ds_ar);
}
何をしようとしているかというと、DOS/4GWに侵入したときのCS、DS、SSの各セグメントに初期状態でセットされているセグメント・セレクタとその属性を確認しておこう、という1点であります。まあ386レベルなのでES、FS、GSなんかまであるけれども今回は無視。
このために使ったのが、Load Access Rights命令であります。
LAR
この命令は16ビット・プロテクトモードにも存在します。ただし、16ビットの286式セグメント・ディスクリプタにくらべると、32ビットモードで386以降のセグメント・ディスクリプタの場合で微妙に読めるビットが拡張されてます。まあ、上位互換性はあるし、コマケー話を気にしなければ以下同文だと。いいのか?
コンパイルして実行
WatcomCがインストールされていれば、以下のコマンドラインでDOS/4GW上で実行されるオブジェクト selector.exe が生成されます。
wcl386 /l=dos4g /d2 selector.c
実行は普通にDOSプロンプトから、selector と打てば内部でよしなにしてくれます。お楽。
2つのセグメント・セレクタが使われてます。どちらもGDT登録です。コードは0x2C番目に登録されているセグメントで実行可能、読み取りも可能。データとスタックは同じ0x2D番目のセグメントを使っていて読み書き可能。
特権は最強の「0」です(小さい方がエライ。)つまりアクセスし放題。すべからくDOSイクステンダというものは、DOSの延長でメモリだけをデカくしたいという要求だけをかなえてくれるものなので、メンドクセー「保護」などは無縁な環境を作ってくれます。そこが付け目?
また、どのセグメントも4Kバイト・ページをグラニュアリティにもっているのは、DOS/4GWだけでなくもともとのFreeDOS自体が、仮想86モードのページングで走っている(メモリのドライバ内でページング機構を管理して、EMMとか実現している)ので、デフォのお約束みたいっす。まあ、仮想記憶とかに踏み込まなければ、別にセグメンテーション・レベルでは何ということもなし。
ともあれプロテクトモード内でのセグメントの様子がちょっと分かった。大丈夫か? まだまだ調べないと分からんことが多そうだが。
