2.四則演算をやってみる
作成日:2004年07月29日
更新日:2005年04月07日
ソースのコメント間違いを修正
nameタグを追加



前回は加算命令だけだったので,今回は四則演算をしてみましょう.

というわけでいきなりソースです.

#include <stdio.h>

int add_int (int a, int b) {
    int c;
    __asm {
        mov    eax, a    // eaxレジスタにaの値をコピー
        add    eax, b    // eaxレジスタとbの値を加算してeaxレジスタに保存
        mov    c, eax    // cにeaxレジスタの値をコピー
    }
    return c;
}

int sub_int (int a, int b) {
    int c;
    __asm {
        mov    eax, a    // eaxレジスタにaの値をコピー
        sub    eax, b    // eaxレジスタからbの値を減算してeaxレジスタに保存
        mov    c, eax    // cにeaxレジスタの値をコピー
    }
    return c;
}

int mul_int (int a, int b) {
    int c;
    __asm {
        mov    eax, a    // eaxレジスタにaの値をコピー
        mul    b         // eaxレジスタとbの値を乗算してeaxレジスタに保存
        mov    c, eax    // cにeaxレジスタの値をコピー
    }
    return c;
}

int div_int (int a, int b) {
    int c;
    __asm {
        mov    eax, a    // eaxレジスタにaの値をコピー
        mov    edx, 0    // edxレジスタの値を0にする
        div    b         // eaxレジスタとbの値を乗算して結果はeaxレジスタに,余りはedxレジスタに保存
        mov    c, eax    // cにeaxレジスタの値をコピー
    }
    return c;
}

int main (void) {
    printf ("4 + 2 = %d\n", add_int (4, 2));
    printf ("4 - 2 = %d\n", sub_int (4, 2));
    printf ("4 * 2 = %d\n", mul_int (4, 2));
    printf ("4 / 2 = %d\n", div_int (4, 2));
    return 0;
}

とまあ,こんな感じです.add_int関数は変更ありません.新たにsub_int関数,mul_int関数,div_int関数が加わっています.

実行結果は次のようになります.


4 + 2 = 6
4 - 2 = 2
4 * 2 = 8
4 / 2 = 2


sub命令に関しては,add命令と使い方はほぼ同じです.add命令と同じく,演算結果は第1オペランドに保存されます.

mul命令は若干の注意が必要です.add命令,sub命令では第1オペランドと第2オペランドとの演算を行うのですが,mul命令のオペランドは1つです.これは,mul命令の第1オペランドはオペランドのサイズに応じてAL,AX,またはEAXレジスタとなるという暗黙の了解があるからです.命令のオペランドが第2オペランドとなります.

div命令はさらに注意が必要です.div命令はAXレジスタ,DX:AXレジスタ,またはEDX:EAXレジスタ内の値をソースオペランドで割り,結果をそれぞれAX(AL:AH),DX:AX,またはEDX:EAXレジスタにストアする,というものです.つまり,32bitの除算ならばEDXレジスタとEAXレジスタの2つを組み合わせた64bitのデータを割り,その商をEAXレジスタに,剰余をEDXレジスタに保存します.EDXレジスタを0で初期化しなければ計算結果は乗算エラーとなり,強制終了してしまいます.

試しにdiv_int関数のmov edx, 0をコメントアウトしてください.エラーが出て強制終了するはずです.また,EDXレジスタを初期化する際の値を0ではなく1にしてみましょう.乗算結果が全く見当違いの値になっているはずです.

では,div_int関数を次のように書き換えてみましょう.

int div_int (int a, int b) {
    int c;
    __asm {
        mov    c, edx
    }
    printf ("%d\n", c);
    __asm {
        mov    eax, a
        mov    edx, 0
        div    b
        mov    c, eax
    }
    return c;
}

私の環境では次のような結果になりました.


4 + 2 = 6
4 - 2 = 2
4 * 2 = 8
4344456
4 / 2 = 2


ごらんの通り,EDXレジスタは初期化していない状態の値は保証されておらず,このEDXレジスタとEAXレジスタを合わせた状態では乗算結果が32bitに収まらず,乗算エラーとなります.



今回新たに使用した命令は次のとおりです.

SUB - 整数減算
 第1オペランドと第2オペランドとの整数減算を行う.減算結果は第1オペランドに代入される.
命令説明
SUB AL,imm8AL からimm8 を減算
SUB AX,imm16AX からimm16 を減算
SUB EAX,imm32EAX からimm32 を減算
SUB r/m8,imm8r/m8 からimm8 を減算
SUB r/m16,imm16r/m16 からimm16 を減算
SUB r/m32,imm32r/m32 からimm32 を減算
SUB r/m16,imm8r/m16 から符号拡張されたimm8 を減算
SUB r/m32,imm8r/m32 から符号拡張されたimm8 を減算
SUB r/m8,r8r/m8 からr8 を減算
SUB r/m16,r16r/m16 からr16 を減算
SUB r/m32,r32r/m32 からr32 を減算
SUB r8,r/m8 r8からr/m8 を減算
SUB r16,r/m16r16 からr/m16 を減算
SUB r32,r/m32r32 からr/m32 を減算

MUL - 整数乗算
 第1オペランドと第2オペランドとの整数乗算を行う.乗算結果は第1オペランドに代入される.第1オペランドは,オペランドのサイズに応じてAL,AX,またはEAX レジスタとなる暗黙のオペランドである.結果は,オペランド・サイズに応じてレジスタAX,レジスタペアDX:AX,またはレジスタペアEDX:EAX にストアされ,積の上位ビットはそれぞれAH,DX,またはEDX に入る.
命令説明
MUL r/m8符号なし乗算(AX ← AL. r/m8)
MUL r/m16符号なし乗算(DX:AX ← AX . r/m16)
MUL r/m32符号なし乗算(EDX:EAX ← EAX. r/m32)

DIV - 整数除算
 AX,DX:AX,またはEDX:EAXレジスタ内の値(被除数)をソースオペランド(除数)で割り,結果をそれぞれAX(AH:AL),DX:AX,またはEDX:EAXレジスタにストアする.ソースオペランドには,汎用レジスタまたはメモリロケーションを使用できる.
 この命令の処理は,以下の表に示すように,オペランドサイズ(被除数/除数)に依存する.
オペランドサイズ被除数除数剰余商の最大値
ワード/バイトAXr/m8ALAH255
ダブルワード/ワードDX:AXr/m16AXDX65,535
クワッドワード/ダブルワードEDX:EAXr/m32EAXEDX2^32-1

命令説明
DIV r/m8AX をr/m8 で符号なし除算(AL ←商、AH ←剰余)
DIV r/m16DX:AX をr/m16 で符号なし除算(AX ←商、DX ←剰余)
DIV r/m32EDX:EAX をr/m32 で符号なし除算(EAX ←商、EDX ←剰余)



次回はloop命令を使った繰り返しの処理なんかをやってみます.



もどる

Gポイントポイ活 Amazon Yahoo 楽天

無料ホームページ 楽天モバイル[UNLIMITが今なら1円] 海外格安航空券 海外旅行保険が無料!