kkim의 Assembly
     

여기서 도움을 많이 받았다...

어셈블리어의 개념

참조: kr wikipedia - 어셈블리어
어셈블리어(영어: assembly language) 또는 어셈블러 언어(assembler language)는 기계어와 일대일 대응이 되는 컴퓨터 프로그래밍의 저급 언어이다.

어셈블리어는 1951년에 개발된 언어이다.

예엣날 예엣적 인간이 기계어를 더 쉽게 이해하기 위해 개발된 언어로,

기계어와 1대1 대응 관계이며 저급언어에 속한다. (기계어랑 어셈블리어 둘밖에 없음 ㅋㅋ)

기계어랑 어셈블리어는 1대1 대응 관계인데,

당시 기계어는 기계마다 달랐으므로 기계마다 어셈블리어도 조금식 다르게 된다.

최근에는 임베디드 분야의 발전으로 용량이 작은 어셈블리어가 다시 인기를 끌고 있다!!

(참고: TIOBE 선정 2021년 2월 언어 랭킹)

어셈블리어의 비효율성

물론 어셈블리어는 기계어보다 훨씬 편하다.

아래 두 명령어가 같은 뜻이라고 생각해 보자.. 기계어 프로그래머는 전부 굇수였던게 분명하다;

addi $0, $1, 10
001000 00001 00000 0000000000001010

다만 어셈블리어가 그때 당시 좋았던 건 그렇다 쳐도 현재까지도 효율적이고 좋은 언어라고 할 수는 없다.

어셈블리어는 명령어가 굉장히 축약되어 있어 처음에 공부할 때 어려움이 많고,

동작 하나하나를 명령해야 하기 때문에 명령어 효율성이 떨어지며,

성능 자동판별 기능도 없기 때문에 속도 측면에서도 좋다고 할 수 없다.

냉장고에서 물을 꺼내 마시는 과정을 어셈블리어와 C언어로 비교해보자.

void		물마시기(void)
{
	bool	bOpen = 냉장고문열기();

	if (bOpen)
	{
		물꺼내기();
		마시기();
		물넣기();
		냉장고문닫기();
	}
}
_asm {
	냉장고앞으로가기
	냉장고문잡기
	냉장고문열기
	문열기성공:
		냉장고내부확인
		손들기
		냉장고안에넣기
		물병잡기
		물병꺼내기
		뚜껑열기
		물컵에따르기
		컵손에들기
		컵에든거마시기
		컵내려놓기
		손들기
		뚜껑닫기
		물병잡기
		냉장고안에넣기
		손꺼내기
		냉장고문닫기
}

어셈블리어의 문법

어셈블리어의 명령어는 command [parameters]의 포맷을 지니고 있다.

이 중 command 부분을 opcode라고 부르고, [parameters]를 operand라고 한다.

operand는 두 개 이상이 올 수 있으며,

이 때 앞의 인자가 목적지(dest)가 되며 뒤의 인자가 출발지(src)가 된다.

더보기

- 조작 명령어 -

call : 함수 호출

ret : call로 호출한 함수를 종료하고 다음 명령줄로 이동

nop : 아무것도 하지 않음

jmp : 분기(라벨) 실행

조건 점프 명령어 : cmp 연산 결과에 따라 jmp

  1) je : cmp A B 에서 A == B 일때 jmp

  2) jne : cmp A B 에서 A != B 일때 jmp

  3) ja : cmp A B 에서 A > B일 때 jmp

  4) jb : cmp A B 에서 A < B일 때 jmp

  5) jae : cmp A B 에서 A >= B일 때 jmp

  6) jae : cmp A B 에서 A <= B일 때 jmp

- 데이터 전송 명령어 -

push : 스택에 값 넣기

pop : 스택에서 값 가져오기

mov : mov param1 param2에서 param2의 값을 param1에 대입

lea : lea param1 param2에서 param2의 주소를 param1에 대입

조건 점프 명령어 : cmp 연산 결과에 따라 jmp

- 산술 명령어 -

inc : 인자의 값 1 증가

dec : 인자의 값 1 감소

add : add param1 param2에서 param2의 값을 param1에 더함

sub : sub param1 param2에서 param2의 값을 param1에 뺌

cmp : cmp param1 param2에서 값을 비교할 때 사용; 위의 조건 점프 명령어와 같이 사용

test : test param1 param2에서 두 인자의 값을 AND 연산함.


명령어 개념 학습

cmp 명령어

cmp 명령어는 [dst]과 [src]의 값을 비교하여 포인터에 저장한다.

dst < src인 경우에는 ZF(Zero Flag)에 0을, CF(Carry Flag)에 1을 저장

dst > src인 경우에는 ZF(Zero Flag)에 0을, CF(Carry Flag)에 0을 저장

dst = src인 경우에는 ZF(Zero Flag)에 1을, CF(Carry Flag)에 0을 저장


jmp 명령어

jmp 명령어는 cmp 명령어 계산 결과(ZF, CF)에 따라 다른 파트로 이동한다.

JA CF = 0 and ZF = 0 dst > src
JAE CF = 0 or ZF = 1 dst >= src
JB CF = 1 dst < src
JBE CF = 1 or ZF = 1 dst <= src
JC CF = 1 JB와 차이를 모르겠네요.
JE ZF = 1 dst = src
JZ ZF = 1 dst = src

레지스터의 개념

레지스터는 프로세서에 있는 고속 메모리이다.

작은 데이터와 중간 결과 같은 값을 저장할 수 있기에, 어셈블리어에서는 변수처럼 사용한다.

범용 레지스터

범용 레지스터는 CPU 내에 위치해 있으며,

연산처리/연산결과/복귀주소 등 작은 데이터를 기억하는 레지스터이다.

 1. 산술-논리 연산을 맡는 레지스터

 EAX

Accumulator Register

가장 자주 사용하는 변수로, 사칙연산과 논리연산에서 주로 사용한다.
 함수의 리턴 값도 저장한다.

 EBX

Base Register

특별한 목적으로 만들어진 레지스터가 아니다.
 공간이 필요할 때 적당히 사용하기도 하고,
 범용 레지스터가 부족할 때 서포팅 용도로도 사용한다.
 메모리 주소를 저장한다고도 한다.

 ECX

Count Register

C는 Count의 의미로써 사용되었다. 반복문에서의 idx 역할을 맡는다.
 다만 실행방식이 조금 다른데, 초기화 값에 n을 넣고 idx--을 한다고 생각하자.
 반복문이 없을 때는 변수로 사용할 수 있다.

 EDX

Data Register

일반적으로 EAX와 함께 연동하여 사용한다.
 큰 수의 계산이나 부호 확장 명령 등에 사용한다.

 2. 메모리 주소를 저장하는 레지스터

 EBP

Base Point Register

Stack의 첫 시작 주소를 저장한다.
 Stack이 소멸하지 않는 한 값이 변하지 않는다.

 ESP

Stack Pointer Register

Stack의 끝 주소를 저장한다.
 Push Pop 명령어를 사용할 때 (Stack의 값을 변경시킬 때) 값이 4씩 변한다.

 ESI

Source Index Register

데이터 조작/복사 시 소스 데이터의 주소를 저장한다.

 EDI

Destination Index Register

데이터 복사 시 목적지의 주소를 저장한다.

 

참고로 함수 앞에 _(언더바)를 붙이는 것은 규약이다!!

요기를 참고해 보자.