○MASMインラインアセンブラ
コメント文
値の表現
レジスタは基本的に以下のものが使用できます。
データ処理32bitレジスタは、8bitのレジスタ4個で構成されています。(下位互換性のため)
nop命令
mov命令(値をコピーさせる命令)
xchg命令(レジスタの値を交換)
movzx命令(サイズが違うレジスタにコピー)
add命令(足し算)
sub命令(引き算)
mul命令(掛け算)
div命令(割り算)
jmp命令
call ret命令(BASICで言うGOSUB RETURNのような感じ)
push pop命令(スタックに保存と取り出し)
条件分岐
インクリメントとデクリメント
ループ
実際にインラインアセンブラを使用する時には
FPU
MMX
■コメント文
アセンブラ内では";"の後ろにコメントを書きます。
もちろんインラインアセンブラですのでC/C++のコメントも使用できます。
_asm{
;-----------コメント文のサンプル-------------
mov eax,a ;変数aの値をレジスタeaxにコピー
mov b,eax ;レジスタeaxの値を変数bにコピー
;-----------------------------------------
//もちろんC++のコメント文も使用できます。
/*Cのコメント文も使用できます。*/
}
■値の表現
文字 'A'
10進数 16
16進数 010H ←先頭に0が必要、先頭に0が無いとAなどが文字とみなされる
8進数 20Q
2進数 10000B
■レジスタは基本的に以下のものが使用できます。
▽データ処理32bitレジスタ
eax Accumulator ←通常の値の格納に使用可能
ebx Base register ←通常の値の格納に使用可能
ecx Count register ←通常の値の格納に使用可能
edx Data register ←通常の値の格納に使用可能
▽アドレス指定32bitレジスタ
esp Stack Pointer
ebp Base Pointer
esi Source Index ←通常のポインタの格納に使用可能
edi Destination Index ←通常のポインタの格納に使用可能
■データ処理32bitレジスタは、8bitのレジスタ4個で構成されています。(下位互換性のため)
32bit | eax |
16bit | ax | bx |
8bit | ah | al | bh | bl |
▽つまりサイズによって次のように書きます
□8bitの場合
char a=10;
char b=0;
_asm{
mov ah,a
mov b,ah
}
□16bitの場合
short a=10;
short b=0;
_asm{
mov ax,a
mov b,ax
}
□32bitの場合
long a=10;
long b=0;
_asm{
mov eax,a
mov b,eax
}
■nop命令
何もしない命令です。
■mov命令(値をコピーさせる命令)
long aの値をeaxレジスタを介してlong bにコピーします。
#include <stdio.h>
int main(char* argv[])
{
long a=10;
long b=0;
_asm{
mov eax,a
mov b,eax
}
printf("%d\n",b);
return 0;
}
▽ポインタのコピー
#include <stdio.h>
int main(char*args[]){
char*a="test";
char*b;
_asm{
mov esi,a
mov b,esi
}
printf("%s\n",b);
return 0;
}
処理結果
test
▽ポインタを進めることもできます。
#include <stdio.h>
int main(char*args[]){
char*a="test";
char*b;
_asm{
mov edi,a
inc edi//インクリメント(1を足す)
mov b,edi
}
printf("%s\n",b);
return 0;
}
処理結果
est
■xchg命令(レジスタの値を交換)
#include <stdio.h>
int main(char*args[]){
long a=10;
long b=20;
_asm{
mov eax,a
mov ebx,b
xchg eax,ebx//レジスタの値を交換します。
mov a,eax
mov b,ebx
}
printf("%d %d\n",a,b);
return 0;
}
■movzx命令(サイズが違うレジスタにコピー)
#include <stdio.h>
int main(char*args[]){
long a=10;
short b;
_asm{
mov eax,a
mov bx,b
movzx bx,eax//32bitレジスタを16bitレジスタにコピー
mov b,bx
}
printf("%d\n",b);
return 0;
}
-----------------------------------------------------
#include <stdio.h>
int main(char*args[]){
long a;
short b=10;
_asm{
mov eax,a
mov bx,b
movzx eax,bx//16bitレジスタを32bitレジスタにコピー
mov a,eax
}
printf("%d\n",a);
return 0;
}
■add命令(足し算)
aとbを足して答えをcに入れます
#include <stdio.h>
int main(char* argv[])
{
long a=10;
long b=10;
long c;
_asm{
mov eax,a
mov ebx,b
add eax,ebx
mov c,eax
}
printf("%d\n",c);
return 0;
}
■sub命令(引き算)
aからbを引いて答えをcに入れます
#include <stdio.h>
int main(char* argv[])
{
long a=20;
long b=10;
long c;
_asm{
mov eax,a
mov ebx,b
sub eax,ebx
mov c,eax
}
printf("%d\n",c);
return 0;
}
■mul命令(掛け算)
aとbを掛け算して答えをcに入れます
#include <stdio.h>
int main(char* argv[])
{
long a=10;
long b=20;
long c;
_asm{
mov eax,a
mov ebx,b
mul ebx //eaxは暗黙的に必ず使用される。
mov c,eax
}
printf("%d\n",c);
return 0;
}
■div命令(割り算)
aとbを割り算して答えをcに入れます
#include <stdio.h>
int main(char* argv[])
{
long a=4;
long b=2;
long c;
long d;
_asm{
mov edx,0//計算に使用されるため必ず初期化
mov eax,a
mov ebx,b
div ebx //eax edx は暗黙的に必ず使用される。
mov c,eax//計算結果
mov d,edx//余り
}
printf("%d\n",c);
return 0;
}
■jmp命令
無条件でジャンプします
#include <stdio.h>
int main(char* argv[])
{
long a=0;
_asm{
mov eax,10
jmp LABEL2//LABEL2に無条件ジャンプ
mov eax,0//ここの処理は飛ばされる
LABEL2://ラベル
mov a,eax
}
printf("%d\n",a);
return 0;
}
処理結果
10
■call ret命令(BASICで言うGOSUB RETURNのような感じ)
#include <stdio.h>
int main(char* argv[])
{
long a=0;
_asm{
mov eax,10
call LABEL1//LAVEL1をコールする
jmp LABEL2//LABEL2に無条件ジャンプ
LABEL1:
mov eax,0
ret//呼び出された場所に戻る
LABEL2://ラベル
mov a,eax
}
printf("%d\n",a);
return 0;
}
処理結果
0
■push pop命令(スタックに保存と取り出し)
#include <stdio.h>
int main(char* argv[])
{
long a,b,c;
_asm{
push 10//内容をスタックに保存する
push 20//内容をスタックに保存する
push 30//内容をスタックに保存する
//pushで保存した内容は必ずpopで取り出す必要があります。
pop a//内容をスタックから取り出す
pop b//内容をスタックから取り出す
pop c//内容をスタックから取り出す
}
printf("%d %d %d\n",a,b,c);
return 0;
}
処理結果
30 20 10
■条件分岐
レジスタ比較命令(cmp)を実行した結果によりラベルにジャンプします。
je ==
jne <>
▽符号なし
ja >
jae >=
jb <
jbe <=
▽符号あり
jg >
jge >=
jl <
jle <=
#include <stdio.h>
int main(char*args[]){
long a=0;
_asm{
mov eax,0
mov ebx,1
cmp eax,ebx//レジスタ比較命令
jne LABEL1//cmpの結果が<>の時LABEL1にジャンプします。
jmp END//無条件でENDにジャンプします。
LABEL1:
mov a,10
END:
}
printf("%d\n",a);
return 0;
}
■インクリメントとデクリメント
#include <stdio.h>
int main(char*args[]){
long a;
long b;
_asm{
mov eax,10
mov ebx,10
inc eax//インクリメント
dec ebx//デクリメント
mov a,eax
mov b,ebx
}
printf("%d %d\n",a,b);
return 0;
}
処理結果
11 9
■ループ
#include <stdio.h>
int main(char*args[]){
_asm{
mov ecx,10//ecxが暗黙的に使用されます
LOOPSTART:
//この中が10回ループします。
loop LOOPSTART//ecxの値から1を引き、値が0になるまでジャンプします。
}
return 0;
}
■実際にインラインアセンブラを使用する時には
すでに前後でレジスタを使用中かもしれないので、
使用するレジスタを退避させたほうが安全だと思われます。
#include <stdio.h>
int main(char*args[]){
//Cによる各種処理
_asm{
push eax//スタックに退避
push ebx
push ecx
push edx
push esi
push edi
//各種処理
pop edi//スタックから復帰
pop esi
pop edx
pop ecx
pop ebx
pop eax
}
//Cによる各種処理
return 0;
}
▼レジスタ退避をすると、ループの真ん中にCの構文を挟むこともできます。
#include <stdio.h>
int main(char*args[]){
long a;
_asm{
push ecx//スタックにecxを退避
mov ecx,10//暗黙的に使用されます
LOOPSTART:
mov a,ecx
push ecx//スタックにecxを退避
}
printf("%d\n",a);
_asm{
pop ecx//スタックからecxを復帰
loop LOOPSTART//ecxの値から1を引き、値が0になるまでジャンプします。
pop ecx//スタックからecxを復帰
}
return 0;
}
処理結果
10
9
8
7
6
5
4
3
2
1
■FPU---------------------------------------------------
FPUレジスタは8段スタック型になっています。
8段以上入れようとすると先に入れたものから消えますし、余分に取り出そうとするとゴミが出てきます
-----------
| st |
-----------
| st(1) |
-----------
| st(2) |
-----------
| st(3) |
-----------
| st(4) |
-----------
| st(5) |
-----------
| st(6) |
-----------
| st(7) |
-----------
▼PUSH POP
floatだと fld fstp だがdoubleだと fldl fstpl になる
#include <stdio.h>
int main(char*args[]){
float i,j;
i=10.0001;
_asm{
fld i //プッシュします
fstp j //ポップします
}
printf("%g\n",j);
return 0;
}
処理結果
10.0001
▼足し算
#include <stdio.h>
int main(char*args[]){
float i,j,k;
i=10.0001;
j=1;
_asm{
fld i //プッシュします
fadd j//スタックの先頭に足します
fstp k //ポップします
}
printf("%g\n",k);
return 0;
}
処理結果
11.0001
▼引き算
#include <stdio.h>
int main(char*args[]){
float i,j,k;
i=10.0001;
j=1;
_asm{
fld i //プッシュします
fsubr j//スタックの先頭と引き算をします
fstp k //ポップします
}
printf("%g\n",k);
return 0;
}
処理結果
-9.0001
▼掛け算
#include <stdio.h>
int main(char*args[]){
float i,j,k;
i=10.1;
j=2;
_asm{
fld i //プッシュします
fmul j//スタックの先頭と掛け算をします
fstp k //ポップします
}
printf("%g\n",k);
return 0;
}
処理結果
20.2
▼割り算
#include <stdio.h>
int main(char*args[]){
float i,j,k;
i=10.1;
j=2;
_asm{
fld i //プッシュします
fdiv j//スタックの先頭と割り算をします
fstp k //ポップします
}
printf("%g\n",k);
return 0;
}
処理結果
5.05
▼絶対値
#include <stdio.h>
int main(char*args[]){
float i,j;
i=-10.01;
_asm{
fld i //プッシュします
fabs //スタックの先頭を絶対値に変換
fstp j //ポップします
}
printf("%g\n",j);
return 0;
}
処理結果
10.01
▼符号の反転
#include <stdio.h>
int main(char*args[]){
float i,j;
i=10.01;
_asm{
fld i //プッシュします
fchs //スタックの先頭の符号の反転
fstp j //ポップします
}
printf("%g\n",j);
return 0;
}
処理結果
-10.01
▼sin
#include <stdio.h>
int main(char*args[]){
float i,j;
i=2;
_asm{
fld i //プッシュします
fsin //スタックの先頭のsinを求めます
fstp j //ポップします
}
printf("%g\n",j);
return 0;
}
処理結果
0.841471
▼cos
#include <stdio.h>
int main(char*args[]){
float i,j;
i=2;
_asm{
fld i //プッシュします
fcos //スタックの先頭のcosを求めます
fstp j //ポップします
}
printf("%g\n",j);
return 0;
}
処理結果
-0.416147
▼√
#include <stdio.h>
int main(char*args[]){
float i,j;
i=2;
_asm{
fld i //プッシュします
fsqrt //スタックの先頭の√を求めます
fstp j //ポップします
}
printf("%g\n",j);
return 0;
}
処理結果
1.41421
■MMX---------------------------------------------------
MMXレジスタ
(注)MMX用の回路は、FPU用の回路と共通になっており、同時に使うことは出来ません。
MMXとFPUモードを切り替える時には、数クロックが無駄になります
MMXレジスタは 64bit幅あります 64bit幅を一つの命令で演算ができます
---------------------------------------
| 64bit |
---------------------------------------
16bit処理命令を使うと 一度に4つの計算が出来ます
---------------------------------------
| 16bit | | | |
---------------------------------------
8bit処理命令を使うと 一度に8つの計算ができます
---------------------------------------
|8bit| | | | | | | |
---------------------------------------
▼8bitの符号なし足し算
#include <stdio.h>
void main(){
unsigned char a[8]={0},b[8]={0},c[8];
a[0]=3;
a[1]=2;
a[2]=3;
a[3]=4;
a[4]=5;
a[5]=250;
a[6]=251;
a[7]=252;
b[0]=4;
b[1]=4;
b[2]=4;
b[3]=4;
b[4]=4;
b[5]=4;
b[6]=4;
b[7]=4;
_asm{
movq mm0,a //64bitデータをMMXレジスタにコピー
movq mm1,b //64bitデータをMMXレジスタにコピー
paddb mm0,mm1 //8bit単位 足し算
movq c,mm0 //64bitデータをMMXレジスタからコピー
emms //MMX命令の終了(必ず必要)
}
for(int i=0;i<8;i++)
printf("%d+%d=%d\n",a[i],b[i],c[i]);
}
処理結果
3+4=7
2+4=6
3+4=7
4+4=8
5+4=9
250+4=254
251+4=255
252+4=0 ←桁あふれをしている
▽上の足し算命令を桁あふれ時には最大値を代入してくれる命令に変更
paddusb mm0,mm1//byte足し算(飽和演算命令)
処理結果
3+4=7
2+4=6
3+4=7
4+4=8
5+4=9
250+4=254
251+4=255
252+4=255 ←飽和している
▼8bitの符号付き足し算
#include <stdio.h>
void main(){
char a[8]={0},b[8]={0},c[8];
a[0]=3;
a[1]=2;
a[2]=3;
a[3]=4;
a[4]=5;
a[5]=6;
a[6]=7;
a[7]=8;
b[0]=-4;
b[1]=-4;
b[2]=-4;
b[3]=-4;
b[4]=-4;
b[5]=-4;
b[6]=-4;
b[7]=-4;
_asm{
movq mm0,a //64bitデータをMMXレジスタにコピー
movq mm1,b //64bitデータをMMXレジスタにコピー
paddsb mm0,mm1 //符号付8bit単位 足し算(飽和演算命令)
movq c,mm0 //64bitデータをMMXレジスタからコピー
emms //MMX命令の終了(必ず必要)
}
for(int i=0;i<8;i++)
printf("%d+%d=%d\n",a[i],b[i],c[i]);
}
処理結果
3+-4=-1
2+-4=-2
3+-4=-1
4+-4=0
5+-4=1
6+-4=2
7+-4=3
8+-4=4
▼8bitの引き算
#include <stdio.h>
void main(){
char a[8]={0},b[8]={0},c[8];
a[0]=3;
a[1]=2;
a[2]=3;
a[3]=4;
a[4]=5;
a[5]=6;
a[6]=-7;
a[7]=-8;
b[0]=4;
b[1]=4;
b[2]=4;
b[3]=4;
b[4]=4;
b[5]=127;
b[6]=127;
b[7]=127;
_asm{
movq mm0,a //64bitデータをMMXレジスタにコピー
movq mm1,b //64bitデータをMMXレジスタにコピー
psubb mm0,mm1 //8bit単位 引き算
movq c,mm0 //64bitデータをMMXレジスタからコピー
emms //MMX命令の終了(必ず必要)
}
for(int i=0;i<8;i++)
printf("%d-%d=%d\n",a[i],b[i],c[i]);
}
処理結果
3-4=-1
2-4=-2
3-4=-1
4-4=0
5-4=1
6-127=-121
-7-127=122 ←飽和している
-8-127=121 ←
▽上の引き算命令を飽和演算命令に変更
psubsb mm0,mm1//byte引き算(飽和演算命令)
処理結果
3-4=-1
2-4=-2
3-4=-1
4-4=0
5-4=1
6-127=-121
-7-127=-128 ←飽和している
-8-127=-128 ←
▼8bit符号なし引き算
#include <stdio.h>
void main(){
unsigned char a[8]={0},b[8]={0},c[8];
a[0]=255;
a[1]=255;
a[2]=255;
a[3]=255;
a[4]=1;
a[5]=2;
a[6]=3;
a[7]=4;
b[0]=1;
b[1]=2;
b[2]=3;
b[3]=4;
b[4]=5;
b[5]=6;
b[6]=7;
b[7]=8;
_asm{
movq mm0,a //64bitデータをMMXレジスタにコピー
movq mm1,b //64bitデータをMMXレジスタにコピー
psubusb mm0,mm1 //8bit単位 符号なし引き算(飽和演算命令)
movq c,mm0 //64bitデータをMMXレジスタからコピー
emms //MMX命令の終了(必ず必要)
}
for(int i=0;i<8;i++)
printf("%d-%d=%d\n",a[i],b[i],c[i]);
}
処理結果
255-1=254
255-2=253
255-3=252
255-4=251
1-5=0 ←飽和している
2-6=0 ←
3-7=0 ←
4-8=0 ←
▼16bitの足し算 引き算
#include <stdio.h>
void main(){
unsigned short a[4]={0},b[4]={0},c[4];
a[0]=65533;
a[1]=65533;
a[2]=65533;
a[3]=65533;
b[0]=1;
b[1]=2;
b[2]=3;
b[3]=4;
_asm{
movq mm0,a //64bitデータをMMXレジスタにコピー
movq mm1,b //64bitデータをMMXレジスタにコピー
paddw mm0,mm1 //16bit単位 足し算
movq c,mm0 //64bitデータをMMXレジスタからコピー
emms //MMX命令の終了(必ず必要)
}
for(int i=0;i<4;i++)
printf("%d+%d=%d\n",a[i],b[i],c[i]);
}
処理結果
65533+1=65534
65533+2=65535
65533+3=0 ←飽和している
65533+4=1 ←
▽上の足し算命令を桁あふれ時には最大値を代入してくれる命令に変更
paddusw mm0,mm1 //16bit単位 符号なし足し算(飽和演算命令)
処理結果
65533+1=65534
65533+2=65535
65533+3=65535
65533+4=65535
▼16bit 符号あり足し算
paddsw mm0,mm1 //16bit単位 符号あり足し算(飽和演算命令)
▼16bit 引き算命令
psubw mm0,mm1 //16bit単位 引き算
psubsw mm0,mm1 //16bit単位 符号あり引き算(飽和演算命令)
psubusw mm0,mm1 //16bit単位 符号なし引き算(飽和演算命令)
▼16bit 掛け算命令
pmullw mm0,mm1 //16bit単位 かけ算
▼シフト命令
▽左シフト
#include <stdio.h>
void main(){
unsigned short a[4]={0},b[4];
a[0]=1;
a[1]=2;
a[2]=4;
a[3]=8;
_asm{
movq mm0,a //64bitデータをMMXレジスタにコピー
psllw mm0,1 //16bit単位 左へ1シフト
movq b,mm0 //64bitデータをMMXレジスタからコピー
emms //MMX命令の終了(必ず必要)
}
for(int i=0;i<4;i++)
printf("%d : %d\n",a[i],b[i]);
}
処理結果
1 : 2
2 : 4
4 : 8
8 : 16
▽右シフト
psrlw mm0,1 //16bit単位 右へ1シフト
処理結果
1 : 0
2 : 1
4 : 2
8 : 4
▽算術右シフト(符号付右シフト)
#include <stdio.h>
void main(){
short a[4]={0},b[4];
a[0]=-1;
a[1]=-2;
a[2]=-4;
a[3]=-8;
_asm{
movq mm0,a //64bitデータをMMXレジスタにコピー
psraw mm0,1 //符号付き右シフト命令
movq b,mm0 //64bitデータをMMXレジスタからコピー
emms //MMX命令の終了(必ず必要)
}
for(int i=0;i<4;i++)
printf("%d : %d\n",a[i],b[i]);
}
処理結果
-1 : -1
-2 : -1
-4 : -2
-8 : -4
▼論理演算
▽AND
#include <stdio.h>
void main(){
unsigned short a[4]={0},b[4]={0},c[4];
a[0]=120;// 2進数では 1111000
b[0]=15;// 2進数では 0001111
_asm{
movq mm0,a //64bitデータをMMXレジスタにコピー
movq mm1,b //64bitデータをMMXレジスタにコピー
pand mm0,mm1 //AND命令
movq c,mm0 //64bitデータをMMXレジスタからコピー
emms //MMX命令の終了(必ず必要)
}
printf("%d\n",c[0]);
}
処理結果
8 ←2進数では 0001000
▽OR
#include <stdio.h>
void main(){
unsigned short a[4]={0},b[4]={0},c[4];
a[0]=120;// 2進数では 1111000
b[0]=15;// 2進数では 0001111
_asm{
movq mm0,a //64bitデータをMMXレジスタにコピー
movq mm1,b //64bitデータをMMXレジスタにコピー
por mm0,mm1 //or命令
movq c,mm0 //64bitデータをMMXレジスタからコピー
emms //MMX命令の終了(必ず必要)
}
printf("%d\n",c[0]);
}
処理結果
127 ←2進数では 1111111
▽XOR
#include <stdio.h>
void main(){
unsigned short a[4]={0},b[4]={0},c[4];
a[0]=120;// 2進数では 1111000
b[0]=15;// 2進数では 0001111
_asm{
movq mm0,a //64bitデータをMMXレジスタにコピー
movq mm1,b //64bitデータをMMXレジスタにコピー
pxor mm0,mm1 //xor命令
movq c,mm0 //64bitデータをMMXレジスタからコピー
emms //MMX命令の終了(必ず必要)
}
printf("%d\n",c[0]);
}
処理結果
119 ←2進数では 1110111
▲トップページ
>
Windows と C++