
회장님의 권유로 해본 동아리발표에서 마지막에 짤막하게 extern를 통해 C언어 함수를 사용하여 코드를 작성하는 것과 syscall에만 의존하여 코드를 작성하는 것을 비교하기 위해 넣어봤던 코드입니다
사실 해당 코드도 다 설명하고 싶었는데 그때 당시 너무 바쁜 관계로 준비가 너무 부실해서 그땐 해당 코드 설명은 스킵했습니다.... 발표하고 너무...현타가 하..
인턴 + 교양팀플 + 학과 과제 및 수업 + 포너블팀 과제 + 포너블팀 발표 + 동아리발표... + 드림핵(?) 정신나간 일정에서 돌아와서 이제라도 정리해보려고 합니다

코드만 단순히 보았을때 두개의 양수에 대한 합을 출력해주는 코드입니다 단순 비교를 위한 코드였기도 하고 구현에만 치중하다보니 코드의양 또한 너무 늘어난 것 같습니다 그래도 백준문제에 제출해보면 정상적으로 맞다고 찍히기 때문에 이대로 버리기는 아까워서 글이라도 남겨놓으려고 합니다

입력을 받게 되면 sys_read로 받다보니 그냥 아스키값으로 메모리(스택에)에 저장됩니다 이때 주의해야 할 점이 정수형태로 저장되는 것이 아니다보니 아스키 값을 정수로 변환시켜주고 이것을 또 더해주어서 올바른 값이 되도록해주어야 합니다
두 수의 길이 또한 정해져있지 않기 때문에 공백과 줄바꿈(0xa)를 통해 구분지어 계산해주어야합니다

main함수의 가장 처음에서는 사용할 스택을 확보한 뒤이를 0으로 초기화시켜주고 syscall을 통해 num1과 num2를 입력 받습니다
12345과 99999를 입력으로 주었다고 가정하였을 때 스택을 확인해보면

정수가 아닌 문자열로 인식되어 저장되는 것을 확인할 수 있습니다

입력을 받은 뒤에 반복문을 돌면서 공백이 나올때까지 즉 num1을 .data영역에 미리 선언해두었던 num1이라는 배열에 byte단위로(한수 씩) 저장시킵니다 왜 여기서 0x30을 빼고 정수 형태로 저장하지 않은 이유는..... 그러게요 왜 그랬지..
그래도 이후에 연산을 진행할때 0x30을 빼주기 때문에 여기서는 넘어가도록 하겠습니다
위처럼 반복문을 돌다 공백을 마주치게 되면 num1_len으로 점프하게 됩니다 이후에 [rbp-0x8]을 1감소 시키는데 이는 첫번째 N번째 자리수의 인덱스를 알아내어 [rbp-0x18]에 저장하기 위함입니다
굳이 왜 이게 필요하냐면 가장 오른쪽의 수는 N 자리수이기 때문에 몇 자리의 수인지를 알 수 없기 때문에 길이를 알게되면 이후 연산에서 아스키코드를 정수로 올바르게 바꿔주기 위함입니다 (이후에 다루긴 합니다)
[rbp-0x8]을 1증가 시킨 이유는 공백은 수에 포함되지 않기 때문에 해당 인덱스(?)를 넘기기 위함입니다

위의 과정을 모두 수행한 뒤 [rbp-0x18]과 num1의 수들을 확인해보면 위와 같이 저장된 것을 확인할 수 있습니다

다음에는 num2_loop이 수행됩니다 지금 생각해보면 num1데이터를 저장하는 배열의 주소와 num2데이터를 저장할 데이터의 주소를 스택에 저장하고 num2를 검사할 때는 스택을 8만 증가 시킨뒤 해당 주소를 사용하였다면 2개의 반복문을 사용하지 안해도 될 것 같은데..... 지금보니 참...오늘은 너무 힘들어서 나중에 시간내서 수정해봐야 될 것 같습니다
여기서는 이전 num1의 반복문과 동일하지만 줄바꿈(0xa)까지 반복문을 돌리면서 byte값을 num2배열에 저장합니다
rbp-0x10에는 num2의 N자리수의 인덱스가 저장되어 있습니다

num2_loop 수행 후 num2의 N의 자리 인덱스와 데이터가 저장된 것을 확인할 수 있습니다

1번째 인자로는 인덱스 2번째 인덱스에서는 주소를 주소 add_num함수를 호출하고 그에 대한 반환 값을 [rbp-0x28]에 저장하게 됩니다

해당 함수에서는 첫번째 인자(rdi)를 1씩 감소시키고 해당 인덱스를 정수로 바꾼 뒤 이를 1, 10, 100, 1000 ... 단위로 곱하여서 이것들의 총 합의 정수 값을 반환 시킵니다

이후에 합을 인자로 make_res를 수행한뒤 res배열에 저장된 문자열을 출력하고 프로그램이 종료됩니다


지금 체력상 하나하나 보기에는 너무 몸이 힘들어서 과정만 보면 더해진 정수를 하나하나 문자로 변환하고 마지막에 줄바꿈(0xa)을 추가한 데이터를 res라는 전역 배열에 저장하고 해당 데이터들의 길이를 반환해주는 함수 입니다
global main
section .text
add_num:
push rbp
mov rbp, rsp
sub rsp, 0x20
xor rax, rax
mov [rbp-0x8], rax ;index
mov [rbp-0x10], rax ; num 1 --> 10 --> 100 ... etc
mov [rbp-0x18], rax ; result
mov [rbp-0x20], rax ; array addr
mov [rbp-0x8], rdi
mov [rbp-0x20], rsi
mov byte[rbp-0x10], 1
add_loop:
mov rax, [rbp-0x8]
cmp rax, 0
jl add_exit
mov rcx, [rbp-0x20]
mov al, byte[rcx + rax]
sub rax, 0x30
mov rbx, [rbp-0x10]
mul rbx
add [rbp-0x18], rax
mov rax, [rbp-0x10]
mov rbx, 0xa
mul rbx
mov [rbp-0x10], rax
dec qword[rbp-0x8]
jmp add_loop
add_exit:
mov rax, qword[rbp-0x18]
add rsp, 0x20
leave
ret
make_res:
push rbp
mov rbp, rsp
sub rsp, 0x20
xor rax, rax
mov [rbp-0x8], rax ; num1 + num2
mov [rbp-0x10], rax ; index
mov [rbp-0x18], rax ; return len(result + "\n")
mov [rbp-0x20], rax
mov [rbp-0x8], rdi
res_loop:
mov rax, [rbp-0x8]
test rax, rax
je make_string
xor rdx, rdx
mov rbx, 0xa
div rbx
mov [rbp-0x8], rax
mov rax, [rbp-0x10]
add dl, 0x30
mov byte[res + rax], dl
inc qword[rbp-0x10]
jmp res_loop
make_string:
mov rax, [rbp-0x10]
mov byte[res + rax], 0xa
inc rax
mov [rbp-0x18], rax
xor rax, rax
mov [rbp-0x8], rax
dec qword[rbp-0x10]
mk_string_loop:
mov rax, [rbp-0x10]
mov rbx, [rbp-0x8]
cmp rax, rbx
jle res_func_exit
xor rcx, rcx
xor rdx, rdx
mov cl, byte[res + rax]
mov dl, byte[res + rbx]
mov byte[res + rax], dl
mov byte[res + rbx], cl
inc qword[rbp-0x8]
dec qword[rbp-0x10]
jmp mk_string_loop
res_func_exit:
mov rax, qword[rbp-0x18]
leave
ret
main:
push rbp
mov rbp, rsp
sub rsp, 0x40
xor rax, rax
mov [rbp-0x8], rax ; num1 index
mov [rbp-0x10], rax ; num2 index || length
mov [rbp-0x18], rax ; num1 length
mov [rbp-0x20], rax ;
mov [rbp-0x28], rax ; ======
mov [rbp-0x30], rax
mov [rbp-0x38], rax
mov [rbp-0x40], rax ; input
mov rax, 0
mov rdi, 0
lea rsi, [rbp-0x40]
mov rdx, 0x2c
syscall ; input num1 num2
num1_loop: ; int i = 0 ;
mov rax, [rbp-0x8] ; while(1){
mov al, byte[rbp-0x40 + rax] ;
cmp al, 0x20 ; if(input[i] == 0x20)
je num1_len ; break;
;
xor rcx, rcx ; num1[i] = input[i];
mov cl, al ;
mov rax, [rbp-0x8] ; i++;
mov byte[num1 + rax], cl ; }
inc qword[rbp-0x8]
jmp num1_loop
num1_len:
mov rax, [rbp-0x8]
dec rax
mov [rbp-0x18], rax ; num1 length
inc qword[rbp-0x8]
num2_loop:
mov rax, [rbp-0x8] ; int j = 0;
mov al, byte[rbp-0x40 + rax] ; while(1){
cmp al, 0xa ;
je num2_len ; if(input[i] == 0xa)
; break;
xor rcx, rcx ;
mov cl, al ; num2[j] = input[i];
mov rax, [rbp-0x10] ; i++;
mov byte[num2 + rax], cl ; j++;
;
inc qword[rbp-0x8] ;
inc qword[rbp-0x10] ; }
jmp num2_loop
num2_len:
dec qword[rbp-0x10] ; num2 length
mov rdi, [rbp-0x18] ; num1 length
lea rsi, [num1] ; num1 address
call add_num
mov [rbp-0x28], rax ; result = num1
mov rdi, [rbp-0x10] ; num2 length
lea rsi, [num2] ; num2 address
call add_num
add [rbp-0x28], rax ; result = num2
mov rdi, [rbp-0x28]
call make_res ; make_res(result)
mov rdx, rax ; length of result + \n
mov rax, 1
mov rdi, 1
lea rsi, [res]
syscall ;print's out the result which it's result number in string
xor rax, rax
leave
ret
section .data
res TIMES 21 db 0
num1 TIMES 21 db 0
num2 TIMEs 21 db 0
처음으로 printf나 scanf를 사용하지 않고 입력출력을 수행하는 코드를 짜봐서 더욱 스택이나 코드가 돌아가는 방식에 대해 이해가 된것 같습니다... 지금 다시 보면서도 쓸데없는 코드들이 보이긴한데 나중에 수정되면 다시 올리는걸로...
이제 다시 포너블하러..

'자료구조, 알고리즘 > x64 assembly' 카테고리의 다른 글
백준 1157 단어 공부 (0) | 2021.11.11 |
---|---|
백준 2675 문자열 반복 (0) | 2021.11.08 |
백준 25577번 숫자의 개수 (0) | 2021.10.31 |
백준 2741 N 찍기 (0) | 2021.10.30 |