この章では、BMSCR構造体の使い方とWin32APIの使い方、第1章で説明を後回しにしたスタックフレームについて説明します。 LOADLIB.DLLを利用したモジュールが公開されているサイトを見ると、Win32APIを利用する事でHSPでもいろいろな事ができるのが分かると思います。 是非Win32APIの使い方をマスターして下さい。
BMSCR構造体には、HSPが管理するウインドウ画面についての情報が格納されています。 DLLにBMSCR構造体へのポインタが渡される場合は、現在の操作先画面(gsel命令で指定したウインドウID)の情報が渡されます。具体的にどんな情報が格納されているかは、HSPの公式サイトにある「HSPからのDLL呼び出し方法リファレンスマニュアル」を参照してください。
PVAL構造体、PVAL2構造体の宣言と同様に、BMSCR構造体を宣言しましょう。 HSPの公式サイトにある「HSPからのDLL呼び出し方法リファレンスマニュアル」の中で、BMSCR構造体は以下のように宣言されています。
#define objkazz 64 typedef struct BMSCR { // Bitmap buffer structure // int flag; // used flag int sx; // X-size int sy; // Y-size int palmode; // palmode HDC hdc; // buffer HDC BYTE *pBit; // bitmap pointer BITMAPINFOHEADER *pbi; // infoheader HBITMAP dib; // bitmap handle(DIB) HBITMAP old; // bitmap handle(OLD) RGBQUAD *pal; // palette table HPALETTE hpal; // palette handle HPALETTE holdpal; // palette handle (old) int pals; // palette entries HWND hwnd; // window handle HANDLE hInst; // Instance of program int infsize; // *pbi alloc memory size int bmpsize; // *pBit alloc memory size // Window object setting // int type; // setting type int wid; // window ID short fl_dispw; // display window flag short fl_udraw; // update draw window int wx,wy,wchg; // actual window size x,y int xx,yy; // buffer view point x,y int lx,ly; // buffer view size x,y int cx,cy; // object cursor x,y int ox,oy,py; // object size x,y,py int texty; // text Y-axis size int gx,gy,gmode; // gcopy size HBRUSH hbr; // BRUSH handle HPEN hpn; // PEN handle HFONT hfont; // FONT handle HFONT holdfon; // FONT handle (old) COLORREF color; // text color code HANDLE hCld[objkazz]; // buttonのhandle int owid[objkazz]; // buttonのjump ID int owb; // handleのindex int textspeed; // slow text speed int cx2,cy2; // slow text cursor x,y int tex,tey; // slow text limit x,y char *prtmes; // slow message ptr } BMSCR; |
「HSPからのDLL呼び出し方法リファレンスマニュアル」では、C言語でHSPのプラグインを作るという前提で解説されています。 従って、上記のプログラムをそのまま書き写してもMASM32は理解してくれません。 MASM32では以下のように記述します。 ちなみにMASM32では、typeという名前は予約語になっていて使えないので、代わりに_typeという名前にしました。 また、cxという名前もcxレジスタと重なっていて使えないので、代わりに_cxという名前にしました。
objkazz equ 64 BMSCR struct ; Bitmap buffer structure ; flag dword ? ;used flag sx dword ? ;X-size sy dword ? ;Y-size palmode dword ? ;palmode hdc dword ? ;buffer HDC pBit dword ? ;bitmap pointer pbi dword ? ;infoheader dib dword ? ;bitmap handle(DIB) old dword ? ;bitmap handle(OLD) pal dword ? ;palette table hpal dword ? ;palette handle holdpal dword ? ;palette handle (old) pals dword ? ;palette entries hwnd dword ? ;window handle hInst dword ? ;Instance of program infsize dword ? ;*pbi alloc memory size bmpsize dword ? ;*pBit alloc memory size ; Window object setting ; _type dword ? ;setting type wid dword ? ;window ID fl_dispw word ? ;display window flag fl_udraw word ? ;update draw window wx dword ? ;actual window size x wy dword ? ;actual window size y wchg dword ? xx dword ? ;buffer view point x yy dword ? ;buffer view point y lx dword ? ;buffer view size x ly dword ? ;buffer view size y _cx dword ? ;object cursor x cy dword ? ;object cursor y ox dword ? ;object size x oy dword ? ;object size y py dword ? ;object size py texty dword ? ;text Y-axis size gx dword ? ;gcopy size gy dword ? ;gcopy size gmode dword ? ;gcopy size hbr dword ? ;BRUSH handle hpn dword ? ;PEN handle hfont dword ? ;FONT handle holdfon dword ? ;FONT handle (old) color dword ? ;text color code hCld dword objkazz dup(?) ;buttonのhandle owid dword objkazz dup(?) ;buttonのjump ID owb dword ? ;handleのindex textspeed dword ? ;slow text speed cx2 dword ? ;slow text cursor x cy2 dword ? ;slow text cursor y tex dword ? ;slow text limit x tey dword ? ;slow text limit y prtmes dword ? ;slow message ptr BMSCR ends |
第1章で説明を後回しにしていた、スタックフレームについて説明します。 第2章と第3章では、レジスタの値を退避しておき、後で元に戻すためにpush命令やpop命令を使いました。 しかしスタックには、この他にも重要な役割があります。
HSPからプラグインを呼び出したり、C言語のプログラムが関数を呼び出したりする時は、引数がスタックにpushされてから呼び出されます。 呼び出された側は、スタックの中身を参照することで引数を参照する事ができます。 「えーっ、第2章や第3章ではそんな事してないよ」と思うかもしれませんが、第1章のサンプルを見直してみてください。 プログラムを簡単にするためのおまじないを使っていない方のプログラム(ASMHSP2.ASM)を見ると、確かにスタックの中身を参照していますね。 まず「push ebp」でebpレジスタを退避しておき、「mov ebp,esp」でespレジスタの値をebpレジスタに代入します。 この時スタックの中身は下の図のようになっています。 この後、「push esi」でesiレジスタの値を退避するとespの値は減少しますが、ebpの値は変化しません。 従って、ebpレジスタを利用して引数を参照する事ができるのです。
|
|
このASMHSP2.ASMを見て、「あれっ?」と思われた方もいるかもしれません。 push ebpがあるのにpop ebpがないからです。 ではどこでebpの値を元に戻しているのかというと、leave命令でebpの値を元に戻しています。 leave命令は、「mov esp,ebp」と「pop ebp」を一度に行ってくれる命令です。
さて、スタックには、値を一時的に退避する、引数の受け渡し、の他にもう一つの役割があります。 それはローカル変数です。 次にプロシージャ(関数)が呼び出された時まで値を覚えておく必要がない一時的に使用する変数は、通常はスタックから確保します。 それには、「push ebp」、「mov ebp,esp」の直後に、ローカル変数として使用するバイト数をespレジスタから引きます。 例えば、ローカル変数を4バイト使用する場合にはespから4を引きます。 この場合、スタックの中身は下の図のようになります。
|
|
このように、スタックの中にある、引数やローカル変数が格納されている領域の事をスタックフレームと言います。 スタックフレームを扱うにはこのような複雑な手順を踏まなければなりませんが、MASM32ではproc命令を使う事でASMHSP.ASMのように自動的に行ってくれます。 引数の受け取りは、既に述べたようにproc命令に引数を書き並べる事で自動的に行う事ができます。 また、ローカル変数の確保もlocal命令(これについては後で述べます)を使う事で自動的に行う事ができます。
BMSCR構造体とWin32APIを利用したサンプルとして、HSP標準のpset命令と同じ働きをするbms_pset命令を作ってみます。 まずは以下のソースを見て下さい。 これは、HSPSDKに付いているサンプル(HSPSDK.CPP)の一部をMASM32に移植したものです。
BMSTEST.ASM | .486 .model flat,stdcall .code option casemap :none include \masm32\include\windows.inc include \masm32\include\gdi32.inc include \masm32\include\user32.inc include hspdll.inc includelib \masm32\lib\gdi32.lib includelib \masm32\lib\user32.lib DLLMain proc p1,p2,p3 mov eax,1 ret DLLMain endp bms_send proc uses ebx ecx esi bmscr:ptr,x,y,sx,sy local hdc,opal mov esi,bmscr cmp [esi].BMSCR.fl_udraw,0 jnz @f ret @@: invoke GetDC,[esi].BMSCR.hwnd mov hdc,eax cmp [esi].BMSCR.hpal,NULL jz @f invoke SelectPalette,hdc,[esi].BMSCR.hpal,0 mov opal,eax invoke RealizePalette,hdc @@: mov ebx,x sub ebx,[esi].BMSCR.xx mov ecx,y sub ecx,[esi].BMSCR.yy invoke BitBlt,hdc,ebx,ecx,sx,sy,[esi].BMSCR.hdc,x,y,SRCCOPY cmp [esi].BMSCR.hpal,NULL jz @f invoke SelectPalette,hdc,opal,0 @@: invoke ReleaseDC,[esi].BMSCR.hwnd,hdc ret bms_send endp bms_pset proc export uses esi bmscr:ptr,xx,yy,dummy mov esi,bmscr invoke SetPixel,[esi].BMSCR.hdc,xx,yy,[esi].BMSCR.color invoke bms_send,esi,xx,yy,1,1 mov eax,0 ret bms_pset endp end DLLMain |
include命令は、HSPのinclude命令と同様に指定したファイルを読み込む命令です。 windows.inc、gdi32.inc、user32.incには、Win32APIの一部が宣言されています。 上記のサンプルで利用しているWin32APIは、これら3つのファイルをインクルードすれば利用できます。 しかし、上記のサンプルで利用していないAPIを利用する場合、利用するAPIによっては他のincファイルもインクルードする必要があります。
hspdll.incはhspdll.hをMASM32用に移植したもので、4.3で示したBMSCR構造体の宣言と、3.4で示したPVAL構造体、PVAL2構造体の宣言が書いてあります。 今後PVAL構造体、PVAL2構造体、BMSCR構造体をMASM32で利用する時は、こちらからhspdll.incをダウンロードしてご利用下さい。
includelib命令は、リンクするライブラリを指定します。 これも、利用するWin32APIによって必要なlibファイルが異なります。 どのWin32APIを使うのにどのlibファイルが必要になるかは、MASM32をインストールしたフォルダ(通常はc:\masm32)の中のLIBというフォルダにある、WIN32API.CSVというファイルに書かれています。
それではbms_psetの中身を見てみましょう。 proc命令の行に、uses esiという宣言がありますね。 実は、このように引数の前にusesと書いてレジスタの名前を書く事で、そのレジスタを自動的に退避してくれるのです。 3章のサンプルのようにproc命令の後にpush命令を書いて、ret命令の前にpop命令を書いても同じ結果になります。 ではなぜ3章ではこの機能を使わなかったかというと、push命令とpop命令について知っておいて欲しかったからです。
「mov esi,bmscr」で、BMSCR構造体へのポインタをesiレジスタに代入しています。 これは3章でPVAL2構造体を扱った時と同じですね。 その次の行を見ると、invokeという見慣れない命令があります。 このinvokeというのは、引数をスタックにpushしてからプロシージャを呼び出す命令です。 push命令を並べて引数をpushしてからcall命令でプロシージャを呼び出しても同じですが、invokeを使った方が見た目がスッキリして見やすくなります。 但し、procやlocalと同様invokeもMASM32特有の擬似命令なので、他のアセンブラでは使えない場合があります。 hspsdk.cppの中のbms_psetを見ると、SetPixelを呼び出してからbms_sendを呼び出していますね。 bmstest.asmの中のbms_psetと見比べると、この2行がinvoke命令を使っている2行と対応しているのが分かると思います。
さて、bms_sendの中身を見てみましょう。 先程説明したlocal命令が出てきました。 local命令では、変数の大きさを明記しなかった場合一つの変数につき4バイトのメモリが確保されます。 従って、hdcとopalという名前の4バイトの変数が確保されます。
|
|
HSPSDK.CPPの方を見ると、まずBMSCR構造体の中のfl_udrawが0かどうかを調べ、0ならば呼び出された場所へreturn命令で戻っていますね。 BMSTEST.ASMの方もやっている事は同じです。 まずcmp命令でBMSCR構造体の中のfl_udrawが0かどうかを調べ、0でなければjnz命令でret命令を飛ばします。 0ならば呼び出された場所へret命令で戻ります。
次にGetDCというWin32APIを呼び出し、戻り値をhdcに代入しています。 戻り値はeaxレジスタに入っているので、mov命令でeaxレジスタの値をhdcに代入します。
次に、BMSCR構造体の中のhpalがNULLであればSelectPaletteとRealizePaletteを呼び出しています。 NULLというのは、windows.incやstdlib.hの中で定義されている定数です。 後で出てくるSRCCOPYも、windows.incやwin.hの中で定義されている定数です。
さて、次のBitBltというWin32APIを呼び出している部分に注目してください。 2番目の引数は、xからBMSCR構造体の中のxxの値を引いた値です。 3番目の引数は、yからBMSCR構造体の中のyyの値を引いた値です。 しかし、invokeの引数に「x-[esi].BMSCR.xx」と書いたり、「y-[esi].BMSCR.yy」と書いたりする事はできません。 引数を予め計算しておく必要があります。 そこで、ebxレジスタとecxレジスタを使って引数を計算しています。
次に、BMSCR構造体の中のhpalがNULLであればSelectPaletteを呼び出します。 次にReleaseDCを呼び出し、bms_sendが呼び出された場所へ戻ります。
|
|