最も簡単な逆アセンブル(バブルソート)

だんだん、難易度を上げていく。とはいっても、まだ、基本情報技術者試験レベル(このくらいのレベルだったと思う)。新人のころに、満点がとれるレベルまで勉強したのだが、うそみたいだ*1
さてさて、まずは、バブルソート(単純交換ソート)のプログラムを書く。さすがに「バブルソートってなんだっけ?」というレベルまでは、落ちていなかった。

#include "stdafx.h"

int a[7] = { 2,3,6,5,1,0,4 };

void swap(int a[], int i, int j) {
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
}

void bubbleSort(int a[], int size) {
    for (int i = 0;  i < size; i++) {
        for (int j = 0; j < i; j++) {
            if (a[i] < a[j]) swap(a, i, j);
        }
    }
}

void printArray(int a[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", a[i]);
    }
    printf("\r\n");
}

void main() {
    int size = sizeof(a) / sizeof(a[0]);
    printArray(a, size);
    bubbleSort(a, size);
    printArray(a, size);
}

早速、逆アセンブル。bubbleSort 関数を見てみる。25 行と短い。なんとかなりそうだ。

ここで、スワップする(値を入れ替える)ときのスタックの様子は、次のようになっている。

もともと、たいした処理をしていないので、ゆっくり読めば、まず理解できる。各行の解説は、図の中に示した通りなので、割愛する。
最後に、ここで出てくる重要な命令*2を、まとめておく。

  • 論理演算命令
    • TEST:AND をとる。演算結果は捨てる(レジスタの値は変化させない)。演算結果に応じて、サインフラグ(SF)、ゼロフラグ(ZF)、パリティフラグ(PF)がセットされる。加えて、オーバーフローフラグ(OF)、キャリーフラグ(CF)がクリアされる(0 になる)
    • XOR:XOR をとる。フラグの変化は、AND、TEST などと同様
  • ジャンプ命令(比較した値が符号ありの 4 バイトデータ)→ フラグは変化しない
    • JG:Jump if Greater(SF, OF が、ともに "0"*3、または、ともに "1"*4、かつ ZF がゼロ*5のとき)
    • JGE:Jump if Greater or Equal(SF, OF が、ともに "0" または、ともに "1"*6
    • JL:Jump if Less(SF, OF の、どちらか一方が "0"、他方が "1")
    • JLE:Jump if Less or Equal(SF, OF の、どちらか一方が "0"、他方が "1"、または、ZF が "1" のとき)
  • その他の命令
    • NOP:何もしない。ただし、アライアント*7を合わせるために、オペランドを指定することがある

そろそろ、まとまった量を読んでみようかな。

*1:それもそのはず。数年間、プログラミングから離れただけで、「コンストラクタってなんだっけ?」というレベルになってしまった。しばらくプログラミングをしていたら、ある程度、感覚は戻ってきたのだが。衝撃だった。

*2:データ転送命令である MOV、算術演算命令である COMP と INC、スタック操作命令である PUSH と POP は、さすがにもういいでしょう。

*3:オーバーフローをおこしておらず、かつ、演算結果が正の場合。

*4:オーバーフローをおこしており、かつ、演算結果が負の場合。

*5:演算結果が "0" ではない場合。

*6:基本は、JGE と同様。結果は "0" でもよいので、ZF の判定がない。

*7:データ型のアラインメントとは何か,なぜ必要なのか? が詳しい。ちゃんと解説してくれている人がいるんだ。