アセンブリ言語 strcmpを書いてみた


追記

追記 : 2021/04/14

  • コメントにて、rbpを使っていることを指摘いただいたので書き直しました。
  • cmpsbの使用については、コメントで書き方を教えて頂きました。
    • rdi, rsiが直接インクリメントされるのでDフラグは関係ないようです。
    • 結果に関しても、movsxを使うことで、raxに格納できました。
  • 参考文献を追記しました。

コード

section .text
    global _ft_strcmp

_ft_strcmp:
.loop:
    movsx   eax, BYTE[rdi]
    movsx   ecx, BYTE[rsi]
    inc     rdi
    inc     rsi
    sub     eax, ecx
    jne     .end
    test    cl, cl
    jne     .loop
.end:
    ret

movsx

返り値のサイズに合わせるため、movsxで拡張して格納し、比較しています。

test

2オペランドの論理積を計算し、結果がゼロならゼロフラグ(ZF)を立てます。
同じclとclを比較しているので、clが0なら0を、1なら1を返します。

テストコード

#include <libc.h>

static void test_strcmp(const char *d, const char *s)
{
    printf("%s : %s\n", d, s);
    int libasm = ft_strcmp(d, s);
    int libc = strcmp(d, s);
    printf("libc : %d\n", libc);
    printf("libasm : %d\n", libasm);
    if (libc == libasm)
        printf(GREEN"OK\n"RESET);
    else
        printf(RED"NG\n"RESET);
}


static void testcase_strcmp()
{
    printf("\n--- test strcmp ---\n");
    const char *d = "01234";
    test_strcmp(d, "01234");
    test_strcmp(d, "012341");
    test_strcmp(d, " 01234");
    test_strcmp("0", d);
    test_strcmp("01", d);
    test_strcmp("012", d);
    test_strcmp("0123", d);
    test_strcmp("01234", d);
    test_strcmp("", d);
    test_strcmp(d, "");
    test_strcmp("", "");
}

int main()
{
    testcase_strcmp();
    return (0);
}

RCXレジスタの使用

文字列をインクリメントさせるための配列の添字として、rcxレジスタを使用した。ループ処理に使われるレジスタです。

RCX
カウンタレジスタの「C」です。繰り返し回数を指定するような命令(シフト、ループ、REPプレフィックス付のストリング型命令)で繰り返し回数の設定に使います。その他は汎用レジスタとして使用できます。

Linuxのシステムコールの呼び出しでRCXの内容は保存されません(破壊されます)。

Assembly Programming on x86-64 Linux (04)

参考

レジスタについて

x64 でのソフトウェア規約 | Microsoft Docs
NASM x64 Assembly
Assembly Programming on x86-64 Linux (04)

ステータスフラグについて

条件ジャンプとフラグ
FLAGS register - Wikipedia
X86アセンブラ/x86アーキテクチャ - Wikibooks

movsxについて

Assembly Programming on x86-64 Linux (07)