そもそも、ビデオカードに関する規格にはIBMが定めたVGAというものがありました。 これは最大で640*480ドットの画面表示を行い、Windowsではセーフモードで起動したとき このモードで画面が表示されます(Win XPは違います)。セーフモードの画面を見れば わかりますが、VGAによる表示は使い物にならないくらい画質が荒いです。そこで、 ビデオカードの各メーカーはVGAを拡張してVGAより高い画質の画面が表示できるように なりました。このようにVGAより高い画質を表示するための規格をSVGA(Super VGA)と いいます。
高い画質の画面が表示できるようになったのはいいことですが、 それぞれのメーカーが独自にVGAを拡張したため各メーカー間で 互換性がなくなってしまい、画面を表示するためにはそのメーカーのビデオカード専用の ドライバを使い、そのドライバでビデオカードを操作させて画面を表示しなければ ならなくなりました。このような状況の中でVESAが生まれました。VESAはビデオカードの 基本的な処理のインターフェイスを共通化を図る団体です。基本的な手続きというのは、 たとえばビデオカードのメーカー名を得る手続きや、VRAM領域のはじめのアドレスを得る 手続きなどのことで、実際の描画に関する手続きなどは関知していません。このような 手続きをBIOSのファンクションで実行できるようにしたものがVBE(VESA BIOS Extension) という規格でまとめられています。
VBEはその名のとおりBIOSにアクセスすることになりますが、CPUがプロテクトモードに 移行しても手続きを実行することができます。そして、VBEが出現したことによる 最大の利益はビデオカードのドライバなしで高解像度の画面を出力できることです。 Windows XPではビデオカードのドライバがない場合、VBEで画面を設定し、VRAMアドレスを 得て画面を出力しているようです。かく言う私もWinXPを再インストールしたとき ビデオカードのドライバをインストールしていないのに高解像度の画面が出てきたのには 驚きました。また、先に書いたようにWinXPではセーフモードで起動してもVGAの画面 出力にならないということは、この理由のためです。
ところで、VBEさえあればそれぞれのビデオカードのドライバが要らないように書いていますが、 ハードウエアのアクセラレーションは使えません。正直私もどこがどうなるのかは 詳しくは知りませんが、要は遅いんです。しかし、このことを差し引いても手軽に (ドライバを組むことを考えれば)高解像度の画面を出力できることは大きいので VBEを使用して画面を出力する価値はあると思います。
コンピュータを起動してから画面を表示するまでの流れはおおよそ次のようになっています。
まず描画モードの設定ですが、ここではディスプレイの横・縦の大きさ、表現する色の数 を決めます。ディスプレイの大きさおよび色の数に応じて番号が決まっており、この番号の ことをモード番号といいます。たとえば、800*600の大きさで24ビットカラーで表示するなら モード番号は0x0115となっています。ディスプレイの描画モードはこのモード番号でもって 設定することができます。
次に描画モードの情報を取得します。描画モードの情報というのは、例えば、1ラインあたりの バイト数はどれくらいか、1ピクセルあたりのビット数はどれくらいか、赤・緑・青の情報は 何ビット目から何ビット目までに入るか、そして、フレームバッファのメモリアドレスは どこにあるか、というような情報を得ます。これらの情報はメモリ上に格納されるので プロテクトモードに入った後も参照することができます。
描画モードの情報があれば、フレームバッファーに画面の内容を書き込んで画面を出力 することができます。
VBEの機能は割り込みの0x10で使用することができます。この割り込みは通常VGAの ファンクションを実行していますが、AHレジスタに0x4Fという値を入れることにより VBEのファンクションを実行することができます。VBEのファンクションが実行された 場合、AHレジスタの値はALレジスタにコピーされます。つまり、実行後のALレジスタの 値が0x4Fであればファンクションが実行されたことになるし、そうでなければファンクションが なかったということになります。そして、AHレジスタには実行後のリザルトコードが 入り、実行に成功すれば0x00が入ります。まとめると次のようになります。
AL == 0x4F | ファンクションが有る |
AL != 0x4F | ファンクションが無い |
AH == 0x00 | 正常に終了した |
AH == 0x01 | 失敗した |
AH == 0x02 | 現在のハードウエアでは実行できない |
AH == 0x03 | 現在のモードでは実行できない |
つまり、実行後のAXレジスタの値が0x004Fじゃ無かったら失敗したと見ていい。
モードの設定はファンクションの0x02で行います。つまりAX=0x4F02にします。そして、 モード番号はBXレジスタに格納しておきます。このとき BXレジスタの14ビット目を立てて おくことを忘れないでください。このビットはリニアフレームバッファーを使用することを 表していて、メモリ上の連続した領域にフレームバッファーを割り当てることができます。 ビットを立てなかった場合はバンクフレームバッファーを使用することになりますが この形式のフレームバッファーは扱いが面倒です。 モード番号とディスプレイの大きさ・色の数は次のように関連付けられています。
モード番号 | 大きさ | 色の数 |
---|---|---|
0x0100 | 640x400 | 256 |
0x0101 | 640x480 | 256 |
0x0102 | 800x600 | 16 |
0x0103 | 800x600 | 256 |
0x0104 | 1024x768 | 16 |
0x0105 | 1024x768 | 256 |
0x0106 | 1280x1024 | 16 |
0x0107 | 1280x1024 | 256 |
0x010D | 320x200 | 約32000色(1:5:5:5) |
0x010E | 320x200 | 約65000色(5:6:5) |
0x010F | 320x200 | 約1678万色(8:8:8) |
0x0110 | 640x480 | 約32000色(1:5:5:5) |
0x0111 | 640x480 | 約65000色(5:6:5) |
0x0112 | 640x480 | 約1678万色(8:8:8) |
0x0113 | 800x600 | 約32000色(1:5:5:5) |
0x0114 | 800x600 | 約65000色(5:6:5) |
0x0115 | 800x600 | 約1678万色(8:8:8) |
0x0116 | 1024x768 | 約32000色(1:5:5:5) |
0x0117 | 1024x768 | 約65000色(5:6:5) |
0x0118 | 1024x768 | 約1678万色(8:8:8) |
0x0119 | 1280x1024 | 約32000色(1:5:5:5) |
0x011A | 1280x1024 | 約65000色(5:6:5) |
0x011B | 1280x1024 | 約1678万色(8:8:8) |
色の数が16色や256色の場合はインデックスカラーでそれ以上の数になるとダイレクトカラーに なっていて、約32000色や約65000色の場合は16ビット、約1678万色の場合は24ビットの 大きさになっています。また、RGBのどの成分が何ビットずつ割り振られているかは (a:r:g:b)で表されており、(5:6:5)の場合は赤が5ビット、緑が6ビット、青が5ビット、と なっています。また、(1:5:5:5)の場合はMSBは使用されず、残りの15ビットを3つの成分で 分けています。
モード情報の取得はファンクション0x01を使用します。得られたモード情報はES:DIから 始まるメモリに格納されていて、次のようになっています。
ModeAttributes | dw | モード属性 |
WinAAttributes | db | ウインドウAの属性 |
WinBAttributes | db | ウインドウBの属性 |
WinGranularity | dw | ウインドウグラニュアリティ |
WinSize | dw | ウインドウサイズ |
WinASegment | dw | ウインドウAの開始セグメント |
WinBSegment | dw | ウインドウBの開始セグメント |
WinFuncPtr | dd | ウインドウ関数へのポインタ |
BytesPerScanLine | dw | スキャンラインのバイト数 |
XResolution | dw | ディスプレイの横の大きさ |
YResolution | dw | ディスプレイの縦の大きさ |
XCharSize | db | 文字1つ分の横の大きさ |
YCharSize | db | 文字1つ分の縦の大きさ |
NumberOfPlanes | db | メモリプレーンの数 |
BitsPerPixel | db | 1ピクセルあたりのビット数 |
NumberOfBanks | db | バンクの数 |
MemoryModel | db | メモリモデル |
BankSize | db | キロバイト単位でのバンクの大きさ |
NumberOfImagePages | db | イメージページの数 |
Reserved | db | 予約、常に1 |
RedMaskSize | db | 赤成分のビット数 |
RedFieldPosition | db | 赤成分のLSBの位置 |
GreenMaskSize | db | 緑成分のビット数 |
GreenFieldPosition | db | 緑成分のLSBの位置 |
BlueMaskSize | db | 青成分のビット数 |
BlueFieldPosition | db | 青成分のLSBの位置 |
RsvdMaskSize | db | 使用されないビットの数 |
RsvdFieldPosition | db | 使用されないビットのLSBの位置 |
DirectColorModeInfo | db | ダイレクトカラーの属性 |
PhysBasePtr | dd | リニアフレームバッファーの開始アドレス |
いろいろと並んでいますが、必要な情報はXResolution, YResolution, BitsPerPixel, BytesPerScanLine, PhysBasePtrくらいです。特にPhysBasePtrはフレームバッファーの 先頭アドレスを表していて、かなり重要な情報です。ただ、注意しなければならないことは この先頭アドレスは物理アドレスであるということです。したがって、プロテクトモードに 移行した後このフレームバッファーにアクセスするときはアドレスの変換を行わなければ なりません。
フレームバッファーのアドレスが得られれば後はそこに画面の内容を書いていくだけです。 VBEのファンクションは一切使用しません。画面の座標は左上が原点になっていて、 (x, y)のピクセルはy * BytesPerScanLine + x * BitsPerPixel/8のアドレスでアクセス できます。これで次のような図がかけます。
この文書を作成するに当たり以下の資料を参考にさせていただきました。