LoginSignup
5
1

More than 1 year has passed since last update.

コンピュータと低レベルなじゃんけんをする

Last updated at Posted at 2022-09-03

概要

やっほーtrimscash dayo~
この記事では低レベルなじゃんけんプログラムを見せ,linuxのx86_64のsyscallでの標準入出力,rdrandの適当な解説していくよ~
image.png

プログラム

gccでコンパイルだけしてldリンクしています
なんか変なとこあったらおしえてくだせえ,,先に謝っておくごめんなさい.

janken.s
.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

でやる.

わかりにくかったら,許して..

テストタヒね!!!課題くそ!!!
まあこんなもんでおわりです,うんこ!!

※バグあったけどめんどいからなおさない
追記:なおした

参考

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1