Capture The Frog

かえるぴょこぴょこw

MENU

アセンブリ言語入門 その2

頭の片隅に置いておくと理解がすすむもの

・バイト→ビット ×8
・ビット→バイト /8
アセンブリとは、マシンコードをアセンブリコードに変換すること

x86 CPUには、8つの汎用レジスタがある。
 EAX・EBX・ECX・EDX・ESP・EBP・ESI・EDI
 これらは全て32ビット(4バイト)のサイズ
 プログラムは、これらのレジスタに32(4バイト),16(2バイト),8(1バイト)ビットの値と         

 してレジスタにアクセスすることができる。


・各レジスタの下位16ビット(2バイト)には
 AX・BX・CX・DX・SP・BP・SI・DI
 これらは、全て16ビットだから16,8ビットの値として、プログラムはレジスタにアク 

 セスできる。
 後で、アセンブリ命令の格納先として出てくるので理解しとく。

 

・ちなみにc言語だと
 1バイト   char
 2バイト   short int
 4バイト   int
         long int

         float
   float(浮動小数点は、EAXなどの汎用レジスタではなく)浮動小数レジスタ(sd・snが

   使用される。 
   8バイト         double
                         long double

  となっている。


・リトルエンディエン方式とは、

 下位バイトは下位アドレスに格納され、後続のバイトはメモリ内の連続した上位アド 

 レスに格納される。
 「リトルエンディエン方式とは、データをバイト単位で並べる際のやりかたの一つ

 で、「最後」のバイトから順番にデータを並べる方式のこと」

  https://wa3.i-3-i.info/word11428.htmlより引用

 

・実行する操作を指定する部分を"オペコード"という
 そして、操作される対象を"オペランド"と呼ぶ

 オペランド デスティネーション, ソース
 命令    格納先,  格納元

 みたいな感じ。
 今回は、デスティネーション(格納先)を先に書くやり方で書いていく。

 この書式をIntel Syntaxと呼ぶ。MASM・NASMで採用されている。


・;の後は、コメント文

 

算術演算

アセンブリ言語加減乗除をやっていきます。
これらの命令は2つのオペランド(ソースとディスティネーション)を使って行います。

加算は、ADD命令
減算は、SUB命令
掛け算は、MUL命令
割り算は、DIV命令を使います。

では、早速やっていきましょう。

 

 

ADD命令(足し算

ADD命令は、ソースとディスティネーションを足し、結果をディスティネーションに格納します。
結果に基づいて、eflagsレジスタのフラグを立てたり、クリアしたりします。
これらのフラグは、条件文で利用することができます。

add eax, 24                 ;eax=eax+24と同じ
add eax, ebx               ;eax=eax+ebxと同じ
add [ebx], 24               ;ebxに指定されているアドレスに格納された値に42を足す。

 

 

SUB命令(引き算

SUB命令は、ディスティネーションからソースを引き算し、結果をディスティネーションに格納します。
結果に基づいて、eflagsレジスタのフラグを立てたり、クリアしたりします。
これらのフラグは、条件文で利用することができます。
また、引き算の結果が
0の場合は、ゼロフラグ(zf
マイナス値になる場合は、キャリーフラグ(cfを設定します。

ebxの指しているメモリの値を24とする。

sub eax, 24               ;eax=eax-24と同じ
sub [ebx], 24             ;ebxは24だから、
                                  ebx=ebx-24
           =0となり、zfが設定される。
sub [ebx], 25             ;実行結果は、-1になるのでcfが設定される。

 

 

MUL命令(掛け算

MUL命令は、オペランドを一つだけ取ります。
そのオペランドは、AL,AX,EAXレジスタのいずれかの内容で掛け算されます。
掛け算の結果が、AX,DX,AX,EDX,EAXレジスタに格納されます。
MUL命令のオペランド
8ビット(1バイトの場合は、ALレジスタが掛け算され、その積がAXレジスタに格納されます。
16ビット(2バイトの場合は、AXレジスタと掛け算され、その積がDX・AXレジスタに格納されます。
32ビット(4バイトの場合は、EAXレジスタと掛け算され、その積がEDX・EAXレジスタに格納されます。
積が2つのレジスタに格納される理由は、2つの値が掛け算されると出力値が入力値よりもはるかに大きくなる場合があるからです。

mul ebx     ;EBXは、EAXレジスタと掛け算され、結果はEDXとEAXに格納され 

          る。
mul bx       ;BXは、AXレジスタと掛け算され、結果はDX・AXレジスタに格納

          される。 

 

 

DIV命令(割り算

DIV命令は、レジスタまたはメモリ参照のいずれかを使い、1つのオペランド(a÷bのbを取ります。
割り算を実行するためには、EDX・EAXレジスタに(a÷bのaを入れる必要があります。
EDXには、最上位のdword値(4バイトの値が入ります。
DIV命令が実行された後、商はEAXに格納され、余りはEDXレジスタに格納されます。

 

div ebx                  ;EBXにより割られた結果は、EDXとEAXに格納されます。

練習問題

mov dword ptr [ebp-5], 16h
mov dword ptr [ebp-8], 5
mov eax, [ebp-5]
add eax, [ebp-8]
mov [ebp-0ch], eax
mov ecx, [ebp-5]
sub ecx, [ebp-8]
mov [ebp-0ch], ecx

 

解法

1,

オペランドのややこしいレジスタの値を同じレジスタ同士で、わかりやすい文字に置きかえます。


mov dword ptr a, 16h
mov dword ptr b, 5
mov eax, a
add eax, b
mov c, eax
mov ecx, a
sub ecx, b
mov c, ecx

 

2,

今までのコメントと同じように書いていきます。
mov dword ptr a, 16h       ;dword値16hをaに格納する。
mov dword ptr b, 5      ;dword値5をbに格納する。
mov eax, a          ;EAXにaを格納する。
add eax, b           ;EAX=EAX+b
mov c, eax             ;cにEAXの値を格納する。
mov ecx, a          ;ECXにaを格納する。
sub ecx, b           ;ECX=ECX-b
mov c, ecx          ;cにECXの値を格納する。

 

 

3,

だいぶ読めてきました。
全てのコメントを擬似言語に直します。
mov dword ptr a, 16h    ;a=dword(16h)
mov dword ptr b, 5     ;b=dword(5)
mov eax, a          ;EAX=a
add eax, b        ;EAX=EAX+b
mov c, eax          ;c=EAX
mov ecx, a        ;ECX=a
sub ecx, b            ;ECX=ECX-b
mov c, ecx           ;c=ECX

 

4,

擬似言語から読み解いていきます。
a=dword(16h)
b=dword(5)
EAX=dword(16h)
EAX=dword(16h)+dword(5)
c=dword(16h)+dword(5)
ECX=dword(16h)
ECX=dword(16h)-dword(5)
c=dword(16h)-dword(5)

 

5,

16進数を10進数に直して、dwordは、プログラムの動きに直接関わってこないので消します。16hのhは、この数字が16進数であると示しているので、10進数で16hは1×16+6×1=22となります。
a=22
b=5
EAX=22
EAX=22+5
c=22+5
ECX=22
ECX=22-5
c=22-5
となります!

 


こんな感じで、アセンブリ言語から高級言語(Cとかに読み解くことができます!

今回は、加減乗除のやり方とアセンブリ言語から高級言語に読み替えることができました。
次回は、ビット演算に関してやっていきます!

qwertytan.hatenablog.jp