백준 25577번 숫자의 개수
문제를 짧게 설명하면 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로 넣어주었다면 이런일이 없었겠지만 뭐.... 그렇죠ㅎㅎ