.model memorymodel (,langtype) (,stackoption)
都立工業高等専門学校(現、東京都立産業技術高等専門学校)を卒業、
機械工学が専攻だった癖に論理回路やオーディオ関係の技術者らしい。
趣味として、音楽関係のフリーソフト開発や、DTM・チップチューン活動をしたり、
オーケストラや吹奏楽団でオーボエを吹いてる人。
.model memorymodel (,langtype) (,stackoption)
当該アセンブリ言語ファイルをアセンブルする時のモデルを設定する。
通常、CPU指定(例: .586 )の次に記述する。
memorymodel
メモリーモデルを示す。
多くのMS-DOS用のC言語コンパイラにおけるメモリモデルと同一であり、
それらにてコンパイルされた*.objファイルとリンクする為に指定する。
尚、32bit版のWindows用アプリケーションのメモリモデルは"FLAT"であり、
Visual C++等でコンパイルされた*.objファイルとリンクする場合は、"FLAT"を指定する。
nearは、その領域が1つのセグメント空間にある事を示す。
code & data combinedがYesの場合は、コード領域とデータ領域が同じセグメント空間にあることを示す。
memorymodel | segment | code | data | code & data combined | Operatin System | segment group |
---|---|---|---|---|---|---|
TINY | 16bit | near | near | yes | (MS-DOS *.com) | _TEXT,_DATA,STACK,_BSS |
SMALL | 16bit | near | near | no | (MS-DOS *.exe) | _DATA,STACK,_BSS |
COMPACT | 16bit | near | far | no | (MS-DOS *.exe) | _DATA,STACK,_BSS |
MEDIUM | 16bit | far | near | no | (MS-DOS *.exe) | _DATA,STACK,_BSS |
LARGE | 16bit | far | far | no | (MS-DOS *.exe) | _DATA,STACK,_BSS |
HUGE | 16bit | far | far | no | (MS-DOS *.exe) | _DATA,STACK,_BSS |
FLAT | 32bit | near | near | yes | (Windows *.exe) | _TEXT,_DATA,STACK,_BSS |
langtype
デフォルトの関数の言語タイプを設定する。
関数規約の詳細は、別途「MASM, VC++ における関数規約」を参照。
langtype | Visual C++における関数規約 |
---|---|
C | __cdecl |
SYSCALL | __syscall (廃止) |
STDCALL | __stdcall |
BASIC | |
FORTRAN | __fortran (廃止) |
PASCAL | __pascal (廃止) |
stackoption
スタック領域の場所を示す。
stackoption | スタック領域の場所 |
---|---|
NEARSTACK | SS=DS |
FARSTACK | SS≠DS |
.startup
MS-DOS用の、スタートアップのコードを生成する。
modelの指定内容によって、生成コードが異なる。
memorymodel=TINYの場合。
org 0100h
@Startup:
memorymodel=TINY,FLAT以外、stackoption=NEARSTACK(SS=DS)の場合
SS==DS(DGROUP)として、SPレジスタを調整するコードを生成する。
@Startup:
mov ax, DGROUP
mov ds, ax
mov bx, ss
sub bx, ax
shl bx, 1 ; If .186 or higher, this is
shl bx, 1 ; shortened to shl bx, 4
shl bx, 1
shl bx, 1
cli ; Not necessary in .286 or higher
mov ss, ax
add sp, bx
sti ; Not necessary in .286 or higher
; statements
END @Startup
memorymodel=TINY,FLAT以外、stackoption=FARSTACK (SS≠DS)の場合
@Startup:
mov dx, DGROUP
mov ds, dx
; statements
END @Startup
.exit expression
MS-DOS用の、シェル(command.com等)に戻るコードを生成する。
mov ah, 04Ch
int 21h
expression
シェールに返すコードを0〜255で指定する。
主に、エラーコードとして用いられ、指定した場合は以下のコードを生成する。
mov ax, 04C00h + expression
int 21h
label PROTO (distance) (langtype) ([,parameter:tag]...)
関数のプロトタイプ宣言及び外部宣言をする事ができる。
INVOKEディレクティブで関数を呼ぶとき、それまでの行に関数が定義されなかった場合はエラーとなる。
PROTOにて、プロトタイプ宣言(若しくは外部宣言)する事で、アセンブラにこの関数がある事を通知できる。
label
関数名を指定する。
distance
関数の場所を指定する。
distance | |
---|---|
near | メモリモデルが、TINY, SMALL, COMPACT, FLATの時に指定可。 |
far | メモリモデルが、MEDIUM, LARGE, HUGEの時に指定可。 |
near16 | 80386以降のCPUで、16bit, 32bitモード混在のアプリケーションを書く時に指定する。 |
far16 | |
near32 | |
far32 |
langtype
関数の呼出規約を指定する。
langtype | 規約(Visual C++) |
---|---|
C | __cdecl |
SYSCALL | __syscall |
STDCALL | __stdcall |
BASIC | |
FORTRAN | __fortran |
PASCAL | __pascal |
parameter:tag
当該関数の引数とその型を指定する。
parameterは引数の名前、tagは引数の型を示す。
配列変数は不可。配列を渡す時は、ポインターで渡す事。
tag | 内容 |
---|---|
BYTE | 1BYTEの符号無し整数 (0〜255) |
SBYTE | 1BYTEの符号付き整数 (-128〜127) |
WORD | 2BYTEの符号無し整数 (0〜65,535) |
SWORD | 2BYTEの符号付き整数 (-32,768〜32,767) |
DWORD | 4BYTEの符号無し整数 (0 〜 4,294,967,295) |
SDWORD | 4BYTEの符号付き整数 (2,147,483,648 〜 +2,147,483,647) |
FWORD | 6BYTEの整数 |
QWORD | 8BYTEの整数 |
TBYTE | 10BYTEの整数 |
struc | strucにて定義された構造体 ※構造体のメンバー全てがスタックにコピーされ渡される。 |
PTR tag | tagへのポインタ(構造体へのポインタも可) |
NEAR PTR tag | tagへのnear ポインタ(構造体へのポインタも可) |
FAR PTR tag | tagへのfar ポインタ(構造体へのポインタも可) |
VARREG | 可変長引数 (記述する時は、引数の一番最後に記述する。) |
label PROC (distance) (langtype) (visibility) (USES reglist) ([,parameter:tag]...)
(LOCAL variable([count]):tag)
(statements)
label ENDP
プロシージャー(関数)ブロックの開始(PROC)と終了(ENDP)を宣言する。
このプロシージャーは、call命令、invoke命令にて呼び出すことが出来る。
label
プロシージャー名(関数名)を指定する。
distance
関数の場所を指定する。
PROTOの項を参照。
langtype
関数の呼出規約を指定する。
PROTOの項を参照。
visibility
関数の属性を指定する。
visibility | 属性 |
---|---|
PRIVATE | 外部から関数を隠す。 |
PUBLIC | 外部から関数が呼ばれる。 |
EXPORT |
USES reglist
関数が呼び出されたときに、最初にスタックに退避するレジスタを指定する。
parameter:tag
当該関数の引数とその型を指定する。
parameterは引数の名前、tagは引数の型を示す。
配列変数は不可。配列を渡す時は、ポインターで渡す事。
引数がある場合、bp,ebpレジスタを自動で退避し、スタックフレームとして使用する。
PROTOの項を参照。
LOCAL variable([count]):tag
関数内のみで使う変数を指定する。
variableは変数の名前、
[count]は要素数がcountの配列変数である事を示し、
tagは引数の型を示す(構造体も可)。
この変数はスタック領域に確保され、関数から戻る時に自動で破棄される。
ローカル変数がある場合、bp,ebpレジスタを自動で退避し、スタックフレームとして使用する。
スタックのサイズが許す限り、いくらでも定義できる。
statements
ここにコードを記述する。
以下の関数を記述した場合、
.186
.model small,stdcall
.dosseg
.code
func proc far uses ax ds di,
para1:word,
para2:far ptr WORD
local var[2]:word
lds di,para2
mov ax,[di]
mov var[sizeof(word)*0],ax
mov ax,para1
mov var[sizeof(word)*1],ax
ret
func endp
END
MASM 5.0以前のアセンブリ言語で記述すると、以下と同等の意味を持つ。
.186
_TEXT segment word public 'CODE'
_TEXT ends
_DATA segment word public 'DATA'
_DATA ends
STACK segment para stack 'STACK'
STACK ends
_BSS segment word public 'BSS'
_BSS ends
DGROUP group _DATA,STACK,_BSS
public _func@6
_TEXT segment word public 'CODE'
assume cs:_TEXT, ds:DGROUP
_func@6:
push bp ;
mov bp, sp ;スタックフレームの作成
add sp, 0FFFCh ;ローカル変数領域の作成
push ax
push ds
push di ;USESで指定したレジスタの保存
lds di, [bp+8] ;ds:di ← 引数"para2"
mov ax, [di]
mov [bp-4], ax ;var[0] ← ax
mov ax, [bp+6] ;ax ← 引数"para1"
mov [bp-2], ax ;var[1] ← ax
pop di
pop ds
pop ax ;USESで指定したレジスタの復帰
leave ;ローカル変数の破棄
retf 6 ;__stdcallなので、引数を破棄して関数から戻る。
END
尚、上のコードを見て解るとおり、最適化はされないので、
パイプラインの効率を気にする、且つ、どうしてもアセンブリ言語で書きたいのであれば、
PROC 〜 ENDPを使わず、自力で最適化して関数を書く方が良い。
又、C言語から呼ぶ場合は、__fastcallを使う手もある。例えば、Visual C++言語にて
extern "C" __fastcall void func(int para1, int para2, int para3);
上記の様に宣言された関数は、アセンブリ言語にて
(extern "C"が無いと、Visual C++独自の名前修飾が行われてしまう。)
.386
.model flat,c
.code
@func@12 proc near syscall,
para3:SDWORD ;この場合、スタックフレームがebpに作成される。
; ecx = para1:SDWORD
; edx = para2:SDWORD
; statement
ret 4 ;スタックに入った分の引数は、破棄する。
@func@12 endp
END
または、
.386
.model flat,c
.code
@func@12 proc near syscall
; ecx = para1:SDWORD
; edx = para2:SDWORD
; [esp+4] = para3:SDWORD ;何かをpushした時は、オフセットに気をつける。
; statement
ret 4 ;スタックに入った分の引数は、破棄する。
@func@12 endp
END
と記述する事で、呼び出すことができる。
尚、エル・エス・アイ・ジャパン社のLSI-C86(Wonder Witch版を除く)における関数規約では、引数はレジスタで渡される。
従って、__cdecl等、MASMで対応しているlangtypeでは記述不可能なので、自力でLSI-C86の関数規約に基づいた関数を書く必要がある。
INVOKE expression ([, arguments]...)
指定された関数規約に基づき、関数expressionを呼ぶ。
expression
関数名を指定する。
尚、関数規約に基づく名前修飾は自動で行われる。
arguments
関数の引数を指定する。
引数には、実数、レジスタ、レジスタのペア(dx::ax等)、アドレス式(ADDRで始まる式)が指定可能。
func PROTO near c, var:far ptr word
と宣言された関数を呼ぶ
invoke func,addr label[si]
は、
push DGROUP //ローカル変数の場合、"push ss"となる。
lea ax,[label + si] //ローカル変数の場合、bpレジスタ経由となるため、
push ax //配列要素はdiかsiレジスタのみが可となる。
call _func
add sp,+4
と変換される。
これにより、他言語で記述したプログラムを簡易的に呼び出すことが出来る。
(※LSI-C86は、引数はレジスタで渡される規約の為、__cdecl等の規約での呼出は不可。)
尚、最適化はしないので、パイプラインの効率を気にするのであれば、
invokeを使わず、自力で関数規約に基づいたコードを書く方がよい。
例えば上記のコードの場合、"lea ax,[n]"の後に"push ax"があり、
初代ペンティアムでは1clockのペナルティーが発生する。
又、連続して関数を呼ぶときに、連続した関数を全て終えてからではなく、
各関数毎にスタックポインタを元に戻すので、その文"add sp,+n"命令が増える事となる。
(これは、スタックの破棄を関数側で行う規約(__stdcall等)を使えば、"ret n"命令で戻ってくる為、関係ない。)
Visual C++ | MASM langtype | 引数 | stack破棄 | 名前修飾 |
---|---|---|---|---|
__cdecl | C | stack (右からpush) | 呼出側 | _Label |
__stdcall | STDCALL | stack (右からpush) | 関数側 | _Lebel@n |
__fastcall | ecx, edx stack (右からpush) | 関数側 | @Label@n | |
__thicall (引数固定長) | stack (右からpush) ecx (this) | 関数側 | _Label@n | |
__thicall (引数可変長) | stack (右からpush) thisは最後にpush | 関数側 | _Label | |
__syscall | SYSCALL | stack (右からpush) | 呼出側 | Label |
__fortran | FORTRAN | stack (左からpush) | 関数側 | Label |
__pascal | PASCAL | stack (左からpush) | 関数側 | Label |
BASIC | stack (左からpush) | 関数側 | Label |
8,16,32[bit]の実値(char,int等) eax
64[bit]の実値 edx:eax
ポインタ・参照 eax
呼び出された関数は、ebp, ebx, edi, esi レジスタを保持しなければならない。
つまり、eax, ecx, edxは破壊して良い。
以下の条件時について、以下の条件を満たす場合、
最適化の為、関数規約を無視し、関数の入出力について以下の最適化を行う。
但し、一つでも、リンク時のコード生成が有効(/GT)でない"*.obj"ファイルがあった場合は、関数呼出規約が守られる。
例えば、アセンブリ言語で書かれたソースからアセンブルされた"*.obj"ファイル等。
つまり、アセンブリ言語で書くよりも、コンパイラとリンカに最適化を任せてしまった方が、
多くの場合は、アセンブリ言語で関数を記述するよりも実行速度が早くなる。
Visual C++の場合、独自の名前修飾が行われる。
修飾された名前には、
等の情報が含まれている。
従って、従来の名前修飾がされている関数(アセンブリ言語で書かれた等)
をC++言語から呼ぶ場合は、extern宣言に"C"を追加する必要がある。
例)
extern "C" void func();
また、C++言語で書かれた関数を、C言語、アセンブリ言語から呼ぶ場合は、
従来の名前修飾修飾にする為、関数にextern "C"と宣言する。
例)
extern "C" __cdecl void func(){
//statement;
}
上のアドレスの『@』と『nifty』の間に『spa.』を追加して下さい。
画像で記載されているアドレスでも届きますが、スパム防止のフィルターを多数導入しています。
本ホームページはリンクフリーです。
リンクは、トップページ
"http://shaw.la.coocan.jp/"
にお願いいたします。