1.2 物理メモリアクセス(その2 プログラム構造)

 ここで紹介するプログラムは,Windowsアプリケーションから物理メモリを直接操作するプログラムの第2段です.このプログラムを使うと,自分の好きな物理アドレスを直接操作できるようになります.

 前節で紹介したものと異なり,どのアドレスでも操作できます.前節のものはWindowsが用意したアドレスしか操作できませんでした.

 サンプルプログラムは小さいですが,内容の理解にはi386以上のCPU(Pentium等)のメモリ管理機能の理解が必要です.もっとも,それがわからなくても,物理メモリを直接操作する方法は理解できます.

1-2-1  プログラムファイルとプログラム構造

 プログラムファイルは表1-3の三つです.基本的に行うことは MD2.CPP を入力することのみです.

 プログラムは,WinMainとWinMainから呼び出されるサブルーチンしかありません(図1-3).WinProcなどはありません,とてもシンプルです.

<図1-3> プログラムの構造


1-2-2  プログラムの解説

 リスト1-2のプログラムは,パソコンのビデオRAMに直接データを書き込むプログラムです.前節のリスト1-1にはない,重要なステートメントについて説明します.

ヘッダーファイル

#include <dos.h>

 windows.hをインクルードするのは当たり前ですが,本プログラムではセグメントレジスタ(注1-3)を参照するので,dos.hもインクルードしておく必要があります.

 プログラムは,WinMain()とCreateSelector()の二つのルーチンからなっています.まずCreateSelector()から説明しましょう.

変数:CreateSelector()

DWORD linerAddress ;

HANDLE selector = 0 ;

struct SREGS sregs ;

 これは,単純な宣言文です.特に変わったところはありません,sregsはdsを参照するので宣言しました.

セグメントレジスタを取得:CreateSelector()

segread( &sregs ) ;

 セグメントレジスタの現在の値を取得し,sregsが指し示す構造体に入れます.後でDSを使用するので現在の値を読み出しています.

セレクタの割り当て:CreateSelector()

if( (selector = AllocSelector( sregs.ds )) )

 AllocSelector関数は,新しいセレクタを割り当てます(注1-4)
 パラメータには,コピーするセレクタを指定します.このパラメータに有効なセレクタを指定すると,指定したセレクタの完全なコピーである新しいセレクタを返します.このパラメータが0のときは、初期化されていない新しいセレクタを返します(注1-5)

 関数が正常に終了した場合は,既存のセレクタのコピー,または初期化されていない新しいセレクタを返します.それ以外の場合は,0が返されます.

リニアアドレスの算出:CreateSelector()

linerAddress = ((DWORD)segment << 4L ) + offset ;

 パラメータで渡されたsegmentとoffsetから,32ビット(DWORD)のリニアなアドレスを計算します.計算方法は,8086のセグメントアドレス:オフセットアドレスの方法と同じです.

セレクタのベースを設定:CreateSelector()

SetSelectorBase( selector, linerAddress ) ;

 SetSelectorBase関数は,セレクタのベースを設定します.selectorは,設定するセレクタを指定します.linerAddressは新しいベース値を指定します.この値は,セレクタが示す先頭のアドレスです(注1-6)

セレクタのリミットを設定:CreateSelector()

SetSelectorLimit( selector, length ) ;

 SetSelectorLimit関数は,セレクタのリミットを設定します.selectorは設定するセレクタを指定します.lengthは設定するセレクタの新しいリミット値を指定します(注1-7)

 これで,CreateSelector()の説明は終わりです.次はWinMain()を説明します.

変数:WinMain()

HANDLE selector = 0 ;

LPSTR lpScreen ;

unsigned int loop ;

 単純な宣言文です.特に変わったところはありません.

Main()CreateSelector()の呼び出し:Win

selector = CreateSelector( 0xA000, 0x0000, (DWORD)0x0000FFFF ) ;

 アクセスしたいアドレスと長さを指定して,前述したCreateSelectorを呼び出します.パラメータは先頭アドレス(セグメント形式),オフセット,長さです.

ポインタを生成:WinMain()

lpScreen = (LPSTR)MAKELONG( 0, selector );

 作成したセレクタからポインタを作り出します.

ビデオRAMへの書き込み:WinMain()

for( loop = 0 ; loop < 0xFFFF ; loop++ )

lpScreen[loop] ^= 0xFF ;

 上のコードはDOSでも見慣れたコードでしょう.A000:0000(セグメント:オフセット)から64Kバイトを0xFFでXORしています.直接VRAMと0xFFでXORを取っています.

セレクタの解放:WinMain()

FreeSelector( selector ) ;

 FreeSelector関数は,AllocSelector関数を使用して割り当てたセレクタを解放します.アプリケーションがこの関数を呼び出した後は,セレクタは無効になり使用不能になります(注1-8).

 以上で細かい説明は終わりです.大体動作が理解できたことと思います.


注:1-3 正確には「セレクタ」です.

注:1-4 この関数は「Windowsのプログラミング規定に反するため,どうしてもしても必要なとき以外はアプリケーションでこの関数を使わないでください」と記載されています.普段は使わない方がいいAPIのようです.事実、Win32(Windows95/98/WindowsNT/Windows2000)では廃止されました。

注:1-5 セレクタに関しては,i386のマニュアルを参照してください.セレクタがわからなくても,メモリ参照のためインデックス,とでも考えれば間違いはありません.

注:1-6 細かい点については,i386のセレクタ(LDTなど)を参照してください.

注:1-7 80286,この値は0x10000未満でなければなりません.

注:1-8 この関数は「Windowsのプログラミング規定に反するため,どうしてもしても必要なとき以外はアプリケーションでこの関数を使わないでください」とう記述があります.AllocSelectorと同様に,Win32(Windows95/98/WindowsNT/Windows2000)では廃止されました。



Copyright 2000 北山 洋幸

新刊のご案内


Copyright 1997-2001 CQ Publishing Co.,Ltd.