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_○○という関数はFizzBuzzFizzBuzz、カウントを表示するための関数で、num2strという関数は10進数の数字を文字列に変換する関数です。

最後に

作り始めた最初、macOSでのシステムコール の呼び方や、ldコマンドでリンクがうまくいかなくて手こずったけれどなんとか作り上げることができました。ただ、何番目まで表示するか入力できず、表示する回数を変えるときはソースコードを直接変えなければならないのでまた別の機会で回数を入力できるようにできたらと思います。

ちなみに実行速度はC言語C++で書いたものの方が速いです。

参考リンク