MacOSのアセンブリでFizzBuzzを書いてみた
アセンブリで何か作りたいと思ったので簡単に作れそうなFizzBuzzを作ってみました。ソースがごちゃごちゃしていて見えにくくなっていますがご了承ください。
FizzBuzzとは
FizzBuzzとは数を1から順番に数えていき、数えている数字が3の倍数のとき「Fizz」、5の倍数のとき「Buzz」、3と5の両方の倍数のとき「FizzBuzz」という言葉遊びです。
実行環境
- macOS Catalina v10.15
- NASM v2.14.02
コード
;fizzbuzz.asm global _main global start section .text ;----- memo ----- ; macos syscall base: 0x2000000 ; syscall list: https: https://github.com/opensource-apple/xnu/blob/master/bsd/kern/syscalls.master num2str: xor rax, rax mov rsi, rax push rax push 0xa add rsi, 2 mov rax, rdi mov rcx, 0xa num2str_loop: xor rdx, rdx div rcx mov rdi, rax add rdi, rdx cmp rdi, 0 jz num2str_loopend inc rsi add rdx, 0x30 push rdx jmp num2str_loop num2str_loopend: mov rax, rsp lea rsp, [rsp + rsi * 8] imul rsi, 8 mov rdi, rsi ret write: mov rdx, rsi ; text length mov rsi, rdi ; text mov rax, 0x2000004 ; write system call mov rdi, 1 ; file descriptor: 1 = stdout syscall ret write_num: call num2str mov rsi, rdi mov rdi, rax call write ret write_fizzbuzz: mov rdi, fizzbuzz mov rsi, 9 call write ret write_fizz: mov rdi, fizz mov rsi, 5 call write ret write_buzz: mov rdi, buzz mov rsi, 5 call write ret mod: xor rdx, rdx mov rax, rdi mov rcx, rsi div rcx mov rax, rdx ret start: mov rbx, 0x01 loop: mov rdi, rbx mov rsi, 0x0F call mod cmp rax, 0 jz show_fizzbuzz mov rdi, rbx mov rsi, 0x03 call mod cmp edx, 0 jz show_fizz mov rdi, rbx mov rsi, 0x05 call mod cmp edx, 0 jz show_buzz mov rdi, rbx call write_num jmp finally show_fizzbuzz: call write_fizzbuzz jmp finally show_fizz: call write_fizz jmp finally show_buzz: call write_buzz jmp finally finally: inc rbx lea rcx, [ebx - 0x01] cmp rcx, 100 jnz loop mov rax, 0x2000001 ; exit xor rdi, rdi ; 0 syscall ; exit(0); section .data fizzbuzz: db "FizzBuzz", 0xa fizz: db "Fizz", 0xa buzz: db "Buzz", 0xa
実行
まずNASMでオブジェクトファイルに変換します
nasm -f macho64 fizzbuzz.asm
-f macho64
というのはmacOS 64bit用のバイナリにするということを表しています。fizzbuzz.asm
はファイル名です。
これを実行するとfizzbuzz.o
というオブジェクトファイルが出来上がります。
ld -macosx_version_min 10.7.10 fizzbuzz.o
最後にリンカを使ってリンクします。macosx_version_min
でバージョンを指定しています。これがないとうまくリンクできませんでした。成功するとa.out
というバイナリが出来上がります。
これを実行してみると以下の結果が得られました。
1 2 fizz 4 buzz fizz 7 8 fizz buzz 11 fizz 13 14 fizzbuzz 16 ・ ・ 長いので省略 ・ ・ 89 fizzbuzz 91 92 fizz 94 buzz fizz 97 98 fizz buzz
動作説明
プログラムはstartラベルから始まります。
ループカウント用のrbxを1に初期化します。
rbxを3と5の最小公倍数である15で割って割り切れるどうか確認します。割り切れたならfizzbuzz
と表示して次のカウントに移ります。
3で割って割り切れたらfizz
と表示し、次のカウントに移ります。
5で割り切れたらbuzz
と表示し、次のカウントに移ります。
3,5,15のどれでも割り切れなかった場合はそのカウント数を表示しています。
表示最大回数に到達したかどうか確認し、到達していれば終了、そうでなければカウントを続けます。
write_○○
という関数はFizz
やBuzz
やFizzBuzz
、カウントを表示するための関数で、num2str
という関数は10進数の数字を文字列に変換する関数です。
最後に
作り始めた最初、macOSでのシステムコール の呼び方や、ldコマンドでリンクがうまくいかなくて手こずったけれどなんとか作り上げることができました。ただ、何番目まで表示するか入力できず、表示する回数を変えるときはソースコードを直接変えなければならないのでまた別の機会で回数を入力できるようにできたらと思います。
ちなみに実行速度はC言語やC++で書いたものの方が速いです。