자료구조, 알고리즘/x64 assembly

백준 25577번 숫자의 개수

lok2h4rd 2021. 10. 31. 23:07

problem


문제를 짧게 설명하면 100보다 크고 100보다 작은 자연수 A, B, C입력하고 이를 곱하여 나온 결과에 0 ~ 9가 포함되어있는 횟수를 각각이 출력하는 프로그램을 작성하는 것입니다

global main
extern scanf
extern printf
	

section .text
	digit_check:
		push rbp
		mov rbp, rsp
		sub rsp, 0x10
		xor rax, rax
		mov [rbp-0x8], rax 	; for loop
		mov [rbp-0x10], rax	
		mov [rbp-0x10], rdi 	; argv_1
	check_loop:	
		mov rax, [rbp-0x8]
		mov rcx, [rbp-0x10]
		cmp rcx, rax
		je end_func 
		inc qword[rbp-0x8]
		jmp check_loop
	end_func:
		inc qword[digit_arr + rax * 4]
		add rsp, 0x10
		leave
		ret
	main:
		push rbp
		mov rbp, rsp
		sub rsp, 0x20
		xor rax, rax
		mov [rbp-0x8], rax	; loop 3
		mov [rbp-0x10], rax	; input
		mov [rbp-0x18], rax	; result
		mov [rbp-0x20], rax	; div_number 10
		mov qword[rbp-0x8], 3
		mov qword[rbp-0x18], 1
		mov qword[rbp-0x20], 0xa
	loop:
		mov rax, [rbp-0x8]
		test rax, rax
		je check_num
		mov rdi, input
		lea rsi, [rbp-0x10]
		call scanf		; input three numbers
	
		mov rax, [rbp-0x10]
		mov rcx, [rbp-0x18]
		mul rcx			; mul num
		mov [rbp-0x18], rax
		dec qword[rbp-0x8]
		jmp loop
	check_num:
		xor rdx, rdx
		mov rax, [rbp-0x18]
		test rax, rax
		je print_setting
		mov rcx, [rbp-0x20]
		div rcx
		mov [rbp-0x18], rax
		mov rdi, rdx
		call digit_check
		jmp check_num

	print_setting:
		xor rax, rax
		mov [rbp-0x8], rax
	print_:
		mov rax, [rbp-0x8]
		cmp rax, 10
		je exit_
		mov rdi, output
		mov eax, dword[digit_arr+ rax * 4]
		xor rsi, rsi
		mov rsi, rax
		call printf
		inc qword[rbp-0x8]
		jmp print_
	exit_:
		xor rax, rax
		leave
		ret

section .data
digit_arr: TIMES 10 dd 0
input: db "%d",00
output: db "%d",10,00

해당 코드는 입력을 받는 반복문을 수행하고 결과를 출력해주는 main함수와 0 ~ 9의 개수를 구하는 digit_check함수로 이루어져 있습니다
여기서 끝내기는 무안해서 코드 흐름만 좀 적어두겠습니다..

		xor rax, rax
		mov [rbp-0x8], rax	; loop 3
		mov [rbp-0x10], rax	; input
		mov [rbp-0x18], rax	; result
		mov [rbp-0x20], rax	; div_number 10
		mov qword[rbp-0x8], 3
		mov qword[rbp-0x18], 1
		mov qword[rbp-0x20], 0xa
	loop:
		mov rax, [rbp-0x8]
		test rax, rax
		je check_num
		mov rdi, input
		lea rsi, [rbp-0x10]
		call scanf		; input three numbers
	
		mov rax, [rbp-0x10]
		mov rcx, [rbp-0x18]
		mul rcx			; mul num
		mov [rbp-0x18], rax
		dec qword[rbp-0x8]
		jmp loop

위 코드는 main함수에서 함수 프롤로그 이후부터 main함수에서 반복문이 수행되는 코드까지 입니다 우선 main함수에서는 [rbp-0x10]에 scanf함수로 데이터를 입력 받고 이것을 [rbp-0x18]에 곱하게 되는 과정을 [rbp-0x8]이 0이 될때까지 즉 3번 반복하게 됩니다(loop 돌기전 [rbp-0x8]에 값을 넣어줌)

	check_num:
		xor rdx, rdx
		mov rax, [rbp-0x18]
		test rax, rax
		je print_setting
		mov rcx, [rbp-0x20]
		div rcx
		mov [rbp-0x18], rax
		mov rdi, rdx
		call digit_check
		jmp check_num

다음 [rbp-0x20]에 10이 들어가 있는데 이 값과 [rbp-0x18] 3개의 정수를 곱한 결과 값을 나누어 나머지가 저장된 rdx레지스터를 rdi에 넣고 digit_check함수를 call하게 됩니다 지금 생각해보면 굳이 [rbp-0x20]에 0xa를 넣어서 사용할 필요가 있었나 싶기도 합니다

	digit_check:
		push rbp
		mov rbp, rsp
		sub rsp, 0x10
		xor rax, rax
		mov [rbp-0x8], rax 	; for loop
		mov [rbp-0x10], rax	
		mov [rbp-0x10], rdi 	; argv_1
	check_loop:	
		mov rax, [rbp-0x8]
		mov rcx, [rbp-0x10]
		cmp rcx, rax
		je end_func 
		inc qword[rbp-0x8]
		jmp check_loop
	end_func:
		inc qword[digit_arr + rax * 4]
		add rsp, 0x10
		leave
		ret


digit_check함수에서는 [rbp-0x10]에는 함수 호출 전에 rdi로 넣어준 값이 들어가게 됩니다 즉 A, B, C를 곱하여 나온 결과 값의 1의 자리수가 됩니다
그래서 해당 함수에서는 [rbp-0x8]이 곱한 결과의 1의 자리 수와 같게 되면 end_func로 가게되는데
여기서 inc qword[digit_arr + rax * 4]은 int digit_arr[1의자리 수]라고 생각하시면 됩니다
그래서 배열을 1증가 시키고 함수를 종료시킵니다

	check_num:
		xor rdx, rdx
		mov rax, [rbp-0x18]
		test rax, rax
		je print_setting
		mov rcx, [rbp-0x20]
		div rcx
		mov [rbp-0x18], rax
		mov rdi, rdx
		call digit_check
		jmp check_num

아 위에서 설명 못한 것이 있는데 다시 생각해보면 나누기하고 함수 호출하여서 해당 값을 증가시켰는데 그 다음 수는 어케할껀데라는 의문이 생길 수 있습니다 물론 코드를 봐서 이미 다 아셨겠지만 div를 한 이후에 rax에는 나눈 값이 들어가 있기 때문에 이것을 다시 [rbp-0x18]로 넣어줌으로써 한자리는 감소하고 다시 10으로 나누어 함수를 호출하는 과정을 반복적으로 수행하게 됩니다 언제까지? 값이 0이 될때까지 그래서 rax(값)이 0이면 print_setting으로 점프하게 됩니다

	print_setting:
		xor rax, rax
		mov [rbp-0x8], rax
	print_:
		mov rax, [rbp-0x8]
		cmp rax, 10
		je exit_
		mov rdi, output
		mov eax, dword[digit_arr+ rax * 4]
		xor rsi, rsi
		mov rsi, rax
		call printf
		inc qword[rbp-0x8]
		jmp print_
	exit_:
		xor rax, rax
		leave
		ret


드디어 마지막입니다 빨리 자고싶니다
print_setting을 해준 이유는 0으로 초기화 시키기 위함이고 0 ~ 9까지 몇번 들어가있는지 printf로 출력한 뒤 main함수가 끝나게 됩니다



* 해당 문제는 digit_check함수에서 해당하는 자리수를 배열에서 증가시킬때 바이트 크기를 지정하지 않아서 8byte씩 즈악하는 바람에 분명 코드에서는 이러면 안되는데 왜 증가되는 배열의 다름 배열 값이 0이 되는거지?? 막 이랬는데
데이터크기 문제였네요 애초에 8byte로 넣어주었다면 이런일이 없었겠지만 뭐.... 그렇죠ㅎㅎ