SKYLIGHT STUDIO

[Assembly] 어셈블리어 본문

Computer Programming/Assembly

[Assembly] 어셈블리어

SKY_L 2023. 10. 19. 17:06

어셈블리?

어셈블리어가 무엇일까?

일단 어셈블Assemble의 의미부터 한번 알아보자.

 

파파고의 정의에 따라 키워드를 정의해보면 대강 집합, 회합, 조립으로 정의해볼 수 있겠다. 그렇게 와닿지는 않는...

 

이제 어셈블리어의 정의를 살펴보자.

영문 위키피디아에 따르면 어셈블리어는 다음과 같다고 할 수 있다.

컴퓨터 프로그래밍에서 어셈블리어는 종종 단순히 어셈블리라고도 하며 일반적으로 ASM 또는 asm으로 약칭되는 어셈블리어는 언어의 명령과 아키텍처의 기계어 코드 명령 사이에 매우 강력한 대응성을 가진 저수준 프로그래밍 언어입니다. 어셈블리어는 일반적으로 기계어 명령 하나당 하나의 문이 있지만 상수, 주석, 어셈블러 지시문...메모리 위치, 레지스터, 매크로도 일반적으로 지원됩니다.

풀이해보자면 어셈블리어는 기계어(Machine Code, 01로 대강 표현된다.)와 일대일 매치가 가능한 저수준(Low-level) 프로그래밍 언어라고 할 수 있다는 것. 사람이 하나하나 0101로 코딩을 할 수는 없으니까...

여기서 저수준(Low-level)은 단순히 컴퓨터 언어의 수준이 저열하다는 것이 아닌, 컴퓨터 하드웨어의 근원(source)에 가깝다는 이야기이다.

 

한번 예를 들어보자면, 사람이 물을 마실 때 어떻게 해야 할까? 한번 고수준언어의 관점으로 생각해보자.

 

void DrinkWater_fromRefrigrator() {
	bool Refri_Open = Refri_Open_Check();
    if(Refri_Open)
    {
    	Take_Water_Out();
        Drink_Water();
    }
}

대강 이 정도쯤 된다. 냉장고 문이 열려있는지 체크(1)하고 물을 꺼내고(2) 마시는(3) 순서.

 

그러면 저수준언어인 어셈블리어적인 관점으로 한번 살펴보자.

_asm{
	냉장고앞으로간다
    냉장고문을잡는다
    냉장고문을연다
  오픈성공:
  	냉장고안을본다
    손을든다
    냉장고안에넣는다
    물병을잡는다
    물병을꺼낸다
뚜겅을연다
물을컵에따른다
컵을손에든다
컵에든것을마신다

티스토리 코드블록 상태가 메롱하구마...

언뜻 보기에도 정신이 아프다. 그래도 한 가지 주요한 차이점을 도출해낼 수는 있을 것이다.

어셈블리어와 고급 언어의 가장 결정적인 차이는 "한 가지 동작"이다. 고급 언어는 3~4가지 동작이면 원하는 결과를 얻어낼 수 있지만 어셈블리는 시선을 냉장고를 맞추고, 손을 내리고, 손잡이를 열고...하나하나의 동작을 모두 지정해줘야 한다는 것.

 

이런 탓에 코드 한두줄만 봐서는 해당 프로시저가 무슨 목적으로 만들어졌는지 전혀 알 수가 없다. 따라서 어셈블리어는 단순한 코드 한두줄보다는 전체적인 흐름을 보는 것이 훨씬 중요하다.

 

어쨌건간에 어셈블리어는 통상적인 경우 사용할 일은 그다지 많지 않다. 선배들 말로는 학부에서 배우는 것이 끝이라고 하시는 분들도 있다. 다만 초소형 하드웨어 모듈나 IOT같은 경우는 결국 어셈블리어를 써먹을 수밖에 있어서 '이론'으로서가 아닌 '언어'로서의 어셈블리어는 아직 꾸준히 수요가 있는 편이다. 포트란도 아직 쓰는 마당에...


Intel / AT&T

어셈블리어는 앞서 언급했듯이 기계어를 인간이 사용하기 쉽게 일대일 치환해주는 언어이다. 따라서 어셈블리어는 CPU가 채택한 명령어 세트(ISA)에 따라 구현이 달라질 수 밖에 없어 통일된 규격이 존재하지 않는다. 일단 우리가 흔히 사용하는 x86-AMD64를 위주로 설명한다.

 

x86-AMD64 플랫폼에서는 어셈블리어 문법이 크게 인텔(Intel) 방식과 AT&T 방식으로 나뉜다. 서로 호환되지 않는 것은 당연하고... 굳이 차이점을 나누어보자면 다음과 같다고 할 수 있겠다.

  Intel AT&T
가독성 (상대적으로) 높음 (상대적으로) 낮음
정보량 (상대적으로) 적음 (상대적으로) 많음
주로 사용되는 플랫폼 Windows, MAC Linux

또 주요한 차이점 하나가 있는데, 명령어 수행 방식이다.

이건 어셈블리어의 명령 포맷을 먼저 학습한 뒤에 생각해보자.


어셈블리의 명령 포맷

우선 x-86 CPU의 기본 구조인 IA-32을 우선적으로 설명한다. 어지간하면 씨퓨는 인텔이나 암드 쓰니까.

우선 기본 형태는 다음과 같다.

명령어 + 인자

명령어는 동작을 수행하도록 하는 명령들--그러니까 mov나 push 같은 놈들. 좀더 엘레강스한 표현으로는 opcode(옵코드)라고도 이른다. 운영체제에 대해 학습한 학부생들은 지겹게 들었을 말들이다.

인자는 명령어 다음에 "어떤 장소로 값을 넣을 것인지?" 혹은 "명령어에 해당하는 값" 등이 된다. 이것 또한 좀 있어보이게 표현하면 오퍼랜드(operand)라고도 한다.

 

자아. 좀 더 구체적인 예를 들어보자.

push 337

옵코드는 push, 337이 오퍼랜드가 되는 것이다.(push는 스택에 값을 넣으라는 명령어)

mov eax, 1

여기서부터 살짝 귀찮아진다. 단순히 오퍼랜드가 2개일 뿐이지만...

게시글을 잘 읽은 사람이라면 당연히 오퍼랜드와 옵코드는 추리해낼 수 있다. 문제는 오퍼랜드들간의 관계.

 

인텔에서 Destination은 앞의 오퍼랜드다. 여기서 앞의 오퍼랜드는 목적지 오퍼랜드라고 한다. 즉 인텔 환경에서는 저 코드가 eax에 1을 넣어라--라는 명령이 되는 것이다 .

 

하지만 AT&T는 그게 반대가 되어버린다. AT&T 환경에서는 저 코드는

1에 eax를 넣어라--라는 명령이 되어버리는 다는 것.

 

일단 어지간해서는 IA-32를 사용하므로 앞의 오퍼랜드가 목적지, 뒤의 오퍼랜드가 출발지라고 숙지해두면 좋다.

 

이것 말고도 인텔과 AT&T의 세부적인 차이점은 꽤 있지만 아무래도 귀찮아져서 여기까지만 적는다.

 


다음 게시글에서는 레지스터에 대해 설명한다.

상당히 귀찮은 개념.

'Computer Programming > Assembly' 카테고리의 다른 글

[Assembly #2] 레지스터  (1) 2023.10.20