어셈블리 반복문( jmp )에 대해서 정리한다.
반복문 : for, while, do-while
- 어셈블리에서 분기문과 반복문의 차이는 jmp를 이용해서 코드를 반복 시켜주느냐 마느냐의 차이이다.
// for문
int i;
for(i = 0; i <= 10; i++) {
printf("%d", i);
}
// while문
int i = 1;
while( i <= 10) {
printf("%d", i);
i++;
}
// do while문
int i = 1;
do {
printf("%d", i);
i++;
} while(i <= 10)
* 어셈블리에서 inc 명령어를 이용하면 레지스터나 메모리의 값을 1씩 증가시켜 줄 수 있다.
( 반대로 dec 명령어를 이용하면 값을 레지스터나 메모리의 1씩 감소 시킬 수 있다.. )
* 어셈블리에서는 for, while, do while 모두 표현하는 방법은 똑같다.
( 단지 do while의 경우에는 코드가 한번 먼저 실행 된다는 차이가 있을 뿐이다.. )
( 반복문과 분기문의 차이는 코드가 반복된다는 차이가 있을뿐 사용하는 명령어는 똑같다.. )
( 반복문을 이용할때는 무한 루프에 빠지지 않도록 조건을 설정해서 해당 조건이 되었을때 레이블 이동과 같은
코드를 작성해줘야 코드가 무한루프 되는걸 막을 수 있다.. )
[실습]
- 키보드로부터 입력받은 문자열을 거꾸로 출력하는 C와 어셈블리 프로그램을 작성
- 표준입력 함수: scanf, gets, fgets
- 문자열 길이는 최대 1024byte
[C 코드]
char buffer[1024];
int len = 0;
int i = 0;
int main() {
gets ( buffer );
while( buffer[len] != '\0' ) {
len++;
}
len--;
for( i = len; i >= 0; i-- ) {
printf("%c", buffer[i]);
}
printf("\n");
return 0;
}
[어셈블리 코드]
extern printf
extern gets
section .data
print_chr db '%c', 00
print_new db 10, 00
section .bss
buffer resb 1024
len resd 1
i resd 1
section .text
global main
main:
push buffer
call gets
while:
mov edx, dword [len]
cmp byte [buffer + edx], 0
jz while_end
inc dword [len]
jmp while
while_end:
dec dword [len]
mov edx, dword [len]
mov dword [i], edx
for:
mov edx, dword [i]
cmp dword [i], 0
jl end
mov edx, dword [i]
mov eax, [buffer + edx]
push eax
push print_chr
call printf
dec dword [len]
jmp for
end:
push print_new
call printf
* 여기서 주의할점은 유효주소를 이용한 연산을 할때 [ ] 안에서 메모리와 메모리는 +를 이용한 연산이 불가능하기 때문에 문자열의 길이를 담고 있는 len 레이블의 값을
레지스터(edx)에 할당시킨다음 연산을 해줘야 한다.
* 위 어셈블리 코드에서는 문자열 입력을 scanf 대신에 gets 함수를 이용해서 입력을 받았다.
( gets는 해당 변수의 주소값을 인자로 갖는다 )
* 리눅스에서는 표준 입/출력을 할때 일정 크기가 될때까지 cache 계속 모아둔다. 그렇기 때문에 일정 크기가 되지 전에 입/출력을 하려면
'\n' 줄바꿈, 즉 엔터키 값을 줘야 해당 값들을 입/출력한다.
댓글