概要
やっほーtrimscash dayo~
この記事では低レベルなじゃんけんプログラムを見せ,linuxのx86_64のsyscallでの標準入出力,rdrandの適当な解説していくよ~
プログラム
gccでコンパイルだけしてldリンクしています
なんか変なとこあったらおしえてくだせえ,,先に謝っておくごめんなさい.
.intel_syntax noprefix
.global main
main:
lea r11, _descriptionText
push r11
call print
pop r11#r11 ni butikonderu noha nantonaku
lea r11, _jankenStartText
push r11
call print
pop r11
call getChar#rax will be destroyed
xor r14, r14
mov r14,rax
sub r14, 0x30
checkHand:
cmp r14, 2
jle checkHand_1
jmp exception
checkHand_1:
cmp r14, 0
jge checkHand_end
jmp exception
checkHand_end:
push r14
call printHand
pop r14
call getRand#RIP rax
mov r15, rax
push r15
call printHand
pop r15
judge:
sub r14, r15
mov rax, r14
mov rdx, 0
add rax, 3
mov r11,3
div r11#rdx..0 draw 1 lose 2 win
drawJudge:
cmp rdx, 0
jnz loseJudge
lea r11, _draw
push r11
call print
pop r11
jmp endJudge
loseJudge:
cmp rdx, 1
jnz winJudge
lea r11, _lose
push r11
call print
pop r11
jmp endJudge
winJudge:
cmp rdx, 2
jnz exception
lea r11, _win
push r11
call print
pop r11
jmp endJudge
exception:
lea r11, _exception
push r11
call print
jmp endJudge
endJudge:
jmp exit
#end main######################
#print hand
#(int(0~2))
printHand:
push r15
push r14
push rbp
mov rbp, rsp
mov r15, [rbp+32]
printHand_case_0:
cmp r15,0
jnz printHand_case_1
lea r14, _GU
push r14
call print
pop r14
jmp printHand_end
printHand_case_1:
cmp r15, 1
jnz printHand_case_2
lea r14, _CHOKI
push r14
call print
pop r14
jmp printHand_end
printHand_case_2:
cmp r15, 2
jnz printHand_end
lea r14, _PA
push r14
call print
pop r14
jmp printHand_end
printHand_end:
pop rbp
pop r14
pop r15
ret
#destory rax
getRand:
push r11
push rdx
mov rax, 0
mov rdx, 0
rdrand rax
mov r11,3
div r11
mov rax, rdx
pop rdx
pop r11
ret
#destroy rax
#input char to rax
getChar:
push rcx
push r11
mov rax, 0#read
mov rdi, 0#stdin
lea r11, [_input]
mov rsi, r11
mov rdx, 2
syscall
xor rax,rax
movb al, _input
pop r11
pop rcx
ret
#print string
#(string_adr)
print:
push rax
push r11
push rcx
push rbp
push r15
push rbp
mov rbp, rsp
mov rcx, 0
print_loop:
mov r11, [rbp+56]
add r11, rcx
movb r15b, [r11]
inc rcx
cmp r15b, 0x0#\0
jnz print_loop#
print_loop_end:
mov rax, 1
mov rdi, 1
mov rdx, rcx
mov rsi, [rbp+56]
syscall
pop rbp
pop r15
pop rbp
pop rcx
pop r11
pop rax
ret
#exit
exit:
mov rax, 60
mov rdi, 1
syscall
.data
_input:
.asciz "00"
_jankenStartText:
.asciz "JanKen~"
_descriptionText:
.asciz "0:GU 1:CHOKI 2:PA\n"
_GU:
.asciz "GU\n"
_CHOKI:
.asciz "CHOKI\n"
_PA:
.asciz "PA\n"
_lose:
.asciz "YOU LOSE...bye!"
_win:
.asciz "YOU WIN!!!bye!"
_draw:
.asciz "DRAW!!WE ARE FRIENDS!!"
_exception:
.asciz "FU*K YOU!!"
syscallでの標準入力
標準入力したい!!いろいろあるだろうけど今日はsyscallでreadを使おう!!
syscallって?
syscallってのはOSが用意してくれているサブルーチンで
raxにsyscall番号を指定しその他レジスタに引数を入れ,syscall
命令を実行することで簡単に呼び出すことができる
以下syscall一覧
readの使いかた
readのsyscall番号は0
なのでraxに0
を入れる
mov rax, 0
引数
レジスタ | rdi | rsi | rdx |
---|---|---|---|
説明 | ファイルディスクリプタ | バッファアドレス | 文字数 |
標準入力をしたい場合
ファイルディスクリプタはstdinつまりrdiに0
を入れればいい
mov rdi, 0
バッファはデータセグメント等に確保しておきそのアドレスを指定する
lea r11, _input
mov rsi,r11
_input:
.ascii " "
rdxには最大何文字まで入力させるかを指定する
mov rdx, 2
まとめると
mov rax, 0 #readのsyscall番号
mov rdi, 0 #stdin
lea r11, _input
mov rsi, r11 #buff adr
mov rdx, 2 #文字数
syscall
.data
_input:
.ascii " "
のようにすればよい
このプログラムではgetCharというルーチンを作っている.
読み込む文字数が2なのは改行を入れるため(そうしないとプログラムが終わった後shellに改行が行く..
#destroy rax
#input char to rax
getChar:
push rcx
push r11
mov rax, 0#read
mov rdi, 0#stdin
lea r11, [_input]
mov rsi, r11
mov rdx, 2
syscall
xor rax,rax
movb al, _input
pop r11
pop rcx
ret
syscallでの標準出力
writeを使う
writeの使い方
syscall番号は1
なのでraxに1
を入れる
引数
レジスタ | rdi | rsi | rdx |
---|---|---|---|
説明 | ファイルディスクリプタ | 文字列のアドレス | 文字数 |
標準出力したい場合.
rdiに1
(stdin)を入れる.
mov rax, 1 #writeのsyscall番号
mov rdi, 1 #stdout
lea r11, _text #string adr
mov rsi, r11 #文字数
syscall
_text:
.ascii "hello unchi!"
このプログラムではprintというサブルーチンを作っている
文字列の長さをカウントしてくれるので文字列のアドレスをpushするだけで使える.
#print string
#(string_adr)
print:
push rax
push r11
push rcx
push rbp
push r15
push rbp
mov rbp, rsp
mov rcx, 0
print_loop:
mov r11, [rbp+56]
add r11, rcx
movb r15b, [r11]
inc rcx
cmp r15b, 0x0#\0
jnz print_loop#
print_loop_end:
mov rax, 1
mov rdi, 1
mov rdx, rcx
mov rsi, [rbp+56]
syscall
pop rbp
pop r15
pop rbp
pop rcx
pop r11
pop rax
ret
rdrand
rdrandとは・・・乱数を生成する回路により乱数を作り返す命令
最近実装された命令で2012年以降のintel製CPU,2015年以降のAMD製のCPUに実装されている.
なのでサポートしているかどうか判定するべきかもしれないが今回は省いている
(参考)
使い方
rdrand rcx
のようにニーモニックの後に乱数を入れたいレジスタを指定するだけ.
このプログラムではgetRandというraxに3を法とした乱数を返すルーチンを作っている.
getRand:
push r11
push rdx
mov rax, 0
mov rdx, 0
rdrand rax
mov r11,3
div r11
mov rax, rdx
pop rdx
pop r11
ret
以上
コンパイルとリンクは
gcc -c janken.s -o janken.o&& ld -e main -o janken janken.o
でやる.
わかりにくかったら,許して..
テストタヒね!!!課題くそ!!!
まあこんなもんでおわりです,うんこ!!
※バグあったけどめんどいからなおさない
追記:なおした
参考