본문 바로가기

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

백준 2675 문자열 반복

problem

 

수행할 case N를 입력하고 각각이 case에서는 입력한 문자열의 각 문자들을 입력한 정수만큼 반복하여 출력시켜주면 됩니다

어셈블리로 삼중으로 반복문을 사용해보니까 코드를 헷갈리는 경우가 있었는데 주석의 중요성을 다시금 느끼는 것 같습니다

global main
extern scanf
extern printf


section .text
	main:
		push rbp
		mov rbp, rsp
		sub rsp, 0x30
	
		xor rax, rax
		mov [rbp-0x8], rax	; N
		mov [rbp-0x10], rax	; input
		mov [rbp-0x18], rax	; for i
		mov [rbp-0x20], rax


		mov rdi, input_N
		lea rsi, [rbp-0x8]
		call scanf		; intput N


	loop:
		mov rax, [rbp-0x8]
		test rax, rax
		je _exit
		
		mov rdi, input
		lea rsi, [rbp-0x10]
		lea rdx, [array]
		call scanf		; input
		xor rax, rax
		mov [rbp-0x20], rax
		jmp loop_index
	inc_index:			; inc index [array + index]
		inc qword[rbp-0x20]
	loop_index:
		xor rax, rax
		mov rcx, [rbp-0x20]
		mov al, byte[array + rcx]
		cmp rax, 0x0		; check if its 0x00
		je next_loop

		xor rax, rax
		mov [rbp-0x18], rax
	loop_print:
		mov rax, [rbp-0x10]
		mov rcx, [rbp-0x18]
		cmp rax, rcx
		je inc_index
	
		xor rax, rax
		mov rcx, [rbp-0x20]
		mov al, [array + rcx]
		mov rdi, output
		mov rsi, rax
		call printf
		inc qword[rbp-0x18]
		jmp loop_print
	next_loop:
		dec qword[rbp-0x8]
		mov rdi, output
		mov rsi, 0xa
		call printf
		jmp loop
	_exit:
		xor rax, rax
		leave
		ret



section .data
array: TIMES 21 db 00
input_N: db "%d",00
input: db "%d %s",00
output: db "%c",00

주요 코드를 하나하나 보기 전에 다시금 리뷰해보면 case를 입력 후 반복할 정수와 문자열을 case만큼 반복하는 반복문이 하나 등장합니다

그리고 배열을 한 바이트씩 증가시키면서 각 바이트가 00 즉 문자열의 끝이 되기 전까지 각 문자를 정수만큼 반복해주는 과정을 이중 반복문으로 작성하였습니다 문자를 출력해주는 코드를 함수로 따로 뺐으면 가독성이 더욱 좋을 법도 한데

 

	main:
		push rbp
		mov rbp, rsp
		sub rsp, 0x30
	
		xor rax, rax
		mov [rbp-0x8], rax	; N
		mov [rbp-0x10], rax	; input
		mov [rbp-0x18], rax	; for i
		mov [rbp-0x20], rax


		mov rdi, input_N
		lea rsi, [rbp-0x8]
		call scanf		; intput N

main함수에서 가장 먼저 [rbp-0x8]에 case N을 scanf함수로 입력을 받습니다

 

	loop:
		mov rax, [rbp-0x8]
		test rax, rax
		je _exit
		
		mov rdi, input
		lea rsi, [rbp-0x10]
		lea rdx, [array]
		call scanf		; input
		xor rax, rax
		mov [rbp-0x20], rax
		jmp loop_index

 

 case N을 입력받은 뒤에 loop가 되는데 test instruction을 통해 [rbp-0x8]이 0이 될 때까지 [rbp-0x10]에 반복할 정수를 입력하고 array에 문자열을 입력하여 줍니다 [rbp-0x8]은 test를 즉 0일 때 main함수를 종료시키는 _exit으로 점프하기 때문에 하위 코드에서 dec qword[rbp-0x8]을 통해 [rbp-0x8]을 1씩 감소시키면서 loop가 진행됩니다

 

	inc_index:					; inc index [array + index]
		inc qword[rbp-0x20]
	loop_index:
		xor rax, rax
		mov rcx, [rbp-0x20]
		mov al, byte[array + rcx]
		cmp rax, 0x0			; check if its 0x00
		je next_loop

		xor rax, rax
		mov [rbp-0x18], rax
	loop_print:
		mov rax, [rbp-0x10]
		mov rcx, [rbp-0x18]
		cmp rax, rcx
		je inc_index
	
		xor rax, rax
		mov rcx, [rbp-0x20]
		mov al, [array + rcx]
		mov rdi, output
		mov rsi, rax
		call printf
		inc qword[rbp-0x18]
		jmp loop_print
	next_loop:
		dec qword[rbp-0x8]
		mov rdi, output
		mov rsi, 0xa
		call printf
		jmp loop

이전 코드 마지막에 jmp loop_index라는 instruction이 있었습니다 이는 초기에 inc_index의 코드를 수행하지 않기 위함인데요 [rbp-0x20]은 array의 index를 증가시키기 위해 사용되기 때문에 초기에는 [array + 0]을 검사해주기 위해 있고 다음 loop부터는 [rbp-0x20]을 1씩 증가함으로써 다음 index를 출력해주기 위해 inc_index를 점프하도록 하고 다음 루프부터는 실행시키도록 하였습니다

 

그래서 loop_index를 보면 문자열의 끝(0)인지를 확인하고 그렇다면 nex_loop로 점프하여 [rbp-0x8]을 1 감소시키고 printf를 통해 줄바꿈을 수행한 뒤 다음 정수와 문자열을 입력받기 위해 loop로 jmp 하게 됩니다

만약 문자열 index의 끝이 아니라면 loop_printf로 가게되는데

 

loop_print에서는 실직적으로 정수만큼 해당 문자열의 문자를 출력해줍니다 그리고 정수만큼 출력이 되었다면 그때 inc_index로 점프하여 index를 1 증가시키고 1 증가시킨 문자열의 문자를 다시 정수만큼 출력시키도록 하였습니다

 

 

***꽤나 코드를 복잡하게 짠 것 같아서 다시 복기하면서도 복잡한 거 같다 싶었는데... 조금 더 가독성 있게 코드를 짜는 방법도 꾸준히 생각해봐야 할 것 같습니다

'자료구조, 알고리즘 > x64 assembly' 카테고리의 다른 글

쌩 어셈블리로 두수 더하기  (0) 2021.12.28
백준 1157 단어 공부  (0) 2021.11.11
백준 25577번 숫자의 개수  (0) 2021.10.31
백준 2741 N 찍기  (0) 2021.10.30