어셈블리 언어

From Hidden Wiki
(Redirected from 어셈블리어)
Jump to navigation Jump to search
* 상위 문서: 프로그래밍 언어/종류


[목차]

Assembly

{{{#!syntax cpp .386 .model flat, c .stack 4096 .data

  message db "Hello, World!", 0Dh, 0Ah, 00h

.code

  extern _heap_init: proc
  extern _mtinit: proc
  extern _ioinit: proc
  extern exit: proc   
  extern printf: proc
  main proc
     ; _heap_init(1)
     push 1
     call _heap_init
     add esp, 4
     ; _mtinit(1)
     push 1
     call _mtinit
     add esp, 4
     ; _ioinit()
     call _ioinit
     ; main()
     push offset message
     call printf
     add esp, 4
     ; exit(0)
     push 0
     call exit
     add esp, 4
     ; return 0
     xor eax, eax
     ret
  main endp
  end main

}}} 위 코드는 Microsoft Macro Assmbler와 WinAPI를 사용하여 만든 Hello, world! (헬로, 월드!)이다. 타겟 머신과 어셈블러에 따라 완전히 다른 어셈블리어 코드가 생성된다. 자세한 것은 문서를 계속 읽어볼 것.

개요

프로그래밍 언어의 하나. 기계 언어(기계어, machine language, machine code)에서 한 단계 위의 언어이며 기계어와 함께 단 둘뿐인 로우레벨(Low Level) 언어에 속한다.[* 로우 레벨은 직역하면 저급언어, 여기서 '저급 언어'란 질이 낮은 언어를 뜻하는 것이 아니라, 보다 컴퓨터에 가까운 언어를 의미한다. C언어는 고급 언어로 분류되지만 포인터를 이용하여 메모리에 직접 접근하는 것은 저급 언어적 특징이다.]

기계어라는 게 컴퓨터 관점에서 바로 읽을 수 있다는 것 빼고는 인간의 관점에서는 사용이 불편한 언어이기 때문에 이를 보완하기 위해 나온 것이 어셈블리어다. 따라서 어셈블리어의 특징은 기계어 1라인당 어셈블리 명령어가 대부분 1라인씩 대응되어 있고 이를 비교적 간단하게 짤 수 있는 어셈블러를 통해 기계어로 변환되도록 한 것이다. 이 때문에 어셈블리어는 고급 언어와 기계어 사이에 있다 하여 '중간 언어'라고도 불린다. 그리고 기계어는 CPU가 채택한 ISA에 따라 다 다르기 때문에 어셈블리어의 명령어 역시 통일된 규격이 없다. 또한 문법 아키텍처에 따라서도 다르고 어셈블러의 종류에 따라서도 문법/매크로 등이 제각각이다.

위에서 말했듯이 어셈블리어는 통일된 규격이 없으므로, 모든 플랫폼에서 공통된 사항은 아니나 특별히 우리는 대부분 현재 x86 을 많이 사용하고 있으므로 부연 설명하자면 x86 CPU 에서는, 문법이 크게 인텔 방식과 AT&T 방식으로 나눠진다. 인텔 방식은 가독성이 뛰어나고, AT&T 방식은 가독성은 떨어지나, 인텔 방식보다 좀더 많은 정보를 포함하고 있다고 선호하는 사람들도 있다.[* 물론 이 두 방식은 어느 정도의 절대적인 가독성 비교를 하기엔 어렵고, 적응의 문제라고 보는 것이 맞다. 먼저 오래 접하고 본 것이 어떤 문법이냐에 따라서 다르다는 말이다.] 사실 간단한 수준이라면 뭘 쓰건 큰 차이는 없다.

Windows 플랫폼 위에서 코딩을 하게 될 경우, 보통 사용하는 IDE들이 인텔 문법을 기본으로 하는 경우가 많으므로 이를 그대로 사용하지만, 오픈소스 진영에서는 스탠다드 컴파일러셋에 포함된 GNU 어셈블러(GAS)가 기본 옵션은 AT&T 방식을 사용하고 인텔 방식은 언제까지나 별도 옵션으로 지원하는 정도이기 때문에 GNU 컴파일러 툴과의 연동성을 위해서라면 부득이하게 AT&T 방식을 사용해야 한다.[* 하지만 요즘에는 한 가지 문법만 지원하는 경우는 거의 없으므로 사실상 취향 문제라고 할 수 있다.]

장점

"1번 레지스터(기억장치)에 들어있는 값에 2(이진수 10)를 더해서 0번 레지스터에 넣어라"라는 명령을 MIPS 아키텍처의 CPU에 내려보자.

* 2진수 기계어: 001000 00001 00000 0000000000001010
* 16진수 기계어: 08 01 00 000A
* 어셈블리어: addi $0, $1, 10 // 뜻: $0 = $1 + 10

단순히 바꾼 것뿐이지만 소리내서 읽을 수 있는 단어가 보이는 등 가독성이 좋아진다. 또한 프로그래머가 따로 주석[* 어셈블러 또는 컴파일러가 코드로 인식하지 않는 부분이다. 프로그래머가 원하는 자연어를 쓸 수 있다. C 계열 언어들 기준으로 //를 사용하는데 가령 a = 10 //b = x 라는 코드에서 a = 10이라는 코드는 컴파일러가 인식을 하지만 b = x라는 코드는 앞에 // 하나 붙였다는 이유만으로 코드로 인식되지 않는다. 주석을 달기 위한 목적으로 만든 수단이기 때문에 절대로 코드로 인식돼서는 안 되기 때문.]을 달 수 있게 되어 한 개 이상의 명령 집합이 통틀어서 어떤 역할을 하는 루틴인지 나름대로 설명을 써 놓을 수 있으니 프로그래밍을 하기 훨씬 수월해졌다.

그리고 어셈블러는 컴파일러에 비해 만들기가 쉽다. CPU를 제조하는 제조업체가 해당 CPU 아키텍처에 맞는 C언어 컴파일러를 제공하지 않는 경우는 있을 수 있지만 어셈블러를 제공하지 않는 경우는 거의 없을 정도다. 어셈블러마저 배포하지 않는 칩은 제조업체가 대놓고 단종시키려고 하는 칩이거나 해당 칩 제조사가 거의 망해간다는 의미로 받아들이면 된다. 보통 칩을 팔아먹을 생각이 있는 제조회사라면 C언어 컴파일러까지는 제공한다.

또한 기계어에 대응되는 어셈블리 명령어 이외에도 메모리 위치나 정렬 등을 할 수 있는 지시어를 쓸 수 있고, 매크로 기능을 이용하여 매크로 호출을 쉽고 편리하게 할 수 있다. 그로 인해 기계어로 직접 코딩할 때와 비교했을 때 보다 많은 소스코드가 포함된 프로젝트를 무리 없이 개발할 수 있게 되었다.

그리고 명령어 수가 적다. CPU 매뉴얼의 부록 부분에 적혀있는 명령어 목록이 전부다. 그 외에는 사용하는 어셈블러의 매크로 문법 정도인데 문법의 종류는 CPU의 종류보다 훨씬 적으므로 한 번 익혀두면 대체로 재사용이 가능하다. 그래서 특정 ISA에 대한 언어를 이해 하는데 들어가는 시간이 매우 짧다.[* 사실 난해한 프로그래밍 언어와 같은 종류에서도 최소한의 명령어만 가지고 튜링 완전인 언어들이 많이 있고, 이런 언어들 역시 명령어를 외우는 것 자체는 어렵지 않다.] 그걸 이용해서 실제 프로그램을 짜는 것이 고급 언어보다 어려울 뿐...

단점

어셈블리어의 생산성이 높아지면서 보다 대규모의 소스 코드를 다룰 수 있는 환경이 조성되었지만 그래봤자 어디까지나 기계어 대비 생산성이 높아졌을 뿐, 고급 언어에 비하면 생산성이 매우 떨어진다.

C언어 같은 고급 언어에 비하면 단어가 심하게 축약되어 있어(addi는 add immediate[* '상수'라는 뜻이다. 윗 단락 예시에서 10을 더했듯이 고정된 수를 더할 때 쓰인다. 기억장치에서 숫자를 불러올 필요 없이 바로(immediately) 더할 수 있기 때문에 immediate라는 이름이 붙었다.]의 약자이다.)[* 물론, C 언어도 만들어질 당시에 에디터의 자동완성 기능 같은 건 상상할 수도 없었기 때문에 오늘날 함수명이나 변수 하나가 심하면 한줄씩 차지하는 언어들에 비하면 타수를 줄이기 위하여 strxfrm(string transformation)이나 fma(fused multiply-add) 등의 라이브러리 함수명처럼 축약형을 애용하는 전통을 따르는 편이다.] 읽기에 좀 편한 수준이 된다. 그리고 CPU 아키텍처의 관점에서 소스 코드를 서술해야 하다 보니 정작 코드 작성자가 원하는 기능을 이해하기 쉽게 서술하기도 어렵다.

또한 어셈블리어는 언어를 이해하는데 들어가는 시간은 짧을지 몰라도 언어를 마스터하는데 들어가는 시간은 CPU 종류별로 천차만별인데, AVR, PIC 등의 단순한 CPU들은 CPU의 구조가 단순하여 사람이 코딩해도 그럭저럭 칩의 퍼포먼스를 전부 끌어낼 수 있으나 x86 등의 고성능 PC 및 워크스테이션용 CPU들은 파이프라인 기법이나 슈퍼스칼라 구조, 캐시 같은 온갖 속도향상 기법들이 도입돼 있어 사람이 그 성능을 전부 끌어내는 게 불가능에 가까워졌다. 현대의 i7 CPU도 옛날 386 시절의 x86 어셈블리 명령셋을 가지고 코딩할 수 있는데 그렇게 만든 프로그램의 퍼포먼스는 그야말로 옛날 펜티엄 수준으로 떨어진다. 그냥 80486 CPU가 3GHz 속도로 동작하는 것과 다를 게 없는 환경이 조성되기 때문이다.

예를 들어 CPU가 택배차이고 임무는 쌀배달이며 기계어 코드가 배송 계획이라고 했을 때, 고급 언어로 배송 계획을 짠다면 택배차가 자전거인지 트럭인지에 따라 한 집씩 왕복하면서 배달할지 한 번 싣고 동네를 돌 지를 컴파일러가 알아서 판단한다. 하지만 어셈블리어로 직접 코딩한다면 택배차가 트럭으로 바뀌었는데도 여전히 트럭에 쌀 한가마니씩만 싣고 한 집씩 왕복하는 배송 계획을 실행한다. 왜냐하면, 고급 언어에서는 배송 계획을 짤 때 프로그래머가 '쌀을 쌀집에서 온 동네에 배송한다'라고 추상적으로 지시하지만 어셈블리어에서는 '쌀 하나를 싣는다. 100호를 간다. 100호에 도착해서 쌀을 내려놓는다. 쌀집으로 돌아온다. 쌀 하나를 싣는다. 101호를 간다...' 라고 구체적으로 지시하기 때문이다. 자전거에는 쌀을 실어봐야 한두 가마니에 불과하니 택배차가 자전거(80386)라면 이 지시는 합리적이다. 하지만 택배차가 트럭(i7)이라면 기름낭비, 시간낭비, 재능낭비일 뿐이다. 컴파일러라면 택배차가 쌀 100가마니를 실을 능력이 된다고 판단하면 '쌀 6가마니를 싣는다. 이동 거리가 최소화되는 순서대로 103, 104, 105, 102, 101, 100호 순으로 방문하면서 쌀을 하나씩 배송한다. 쌀집으로 돌아온다.' 라는, 최적화된 배송 계획을 짠다.[* 심지어 지금은 요청이 없지만 배송 중에 200번대 집에서도 배송 요청이 들어오곤 한다는 것까지 알아내서 이를 예측해 미리 200번대 호수에 배송할 쌀까지 실어놓고 동네를 도는 배송 계획도 짤 줄 안다! 전문 용어로는 분기 예측, 슈퍼스칼라 예측 등으로 불린다. 정확히는 CPU가 하는 일이지만 컴파일러가 보조해 주지 않으면 작동하지 않는 기능이다.] 쌀집이 망해서 트럭이 도로 자전거가 돼도, 장사가 흥해서 트럭이 두 대가 돼도 컴파일러는 언제나 퍼포먼스를 최대한 발휘하는 배송 계획을 작성해 준다. 왜냐면, 프로그램이 지시하는 것이 '쌀을 온 동네에 배송한다'이기 때문이다. 고급언어로는 무엇을 하라고 지시하는 게 가능하지만 어셈블리어는 어떻게 하라고만 지시할 수 있어서 이런 문제가 발생한다.

고급 언어의 추상화 레벨이 높아질수록 이 지시도 점점 더 추상적이 되어 간다. 여기서 더 추상화 단계가 올라가다 보면 '배송한다' 하나만 남고 다 지워질 수도 있다. 배송하는 게 쌀인지 고기인지도 중요치 않고, 배송 범위가 동네인지 전국인지도 중요치 않고, 쌀집이 어디 있는지도 중요치 않게 되어 버린다. 프로그래머는 그저 '배송하라'고만 시켰고, 컴파일러는 알아서 뭘 언제 얼마나 어떻게 배송할지 스스로 판단한다. 물론 컴파일러가 그 추상화 레벨을 감당할 수 있느냐는 전제 조건이 붙는다. 추상화가 덜 된 컴파일러한테 '배송하라'고만 시키면 '뭐라고요?' 라고 물을(컴파일 실패) 것이다.

결국 어셈블리어는 이미 90년대 들어오면서부터 그 사용 빈도는 고급 언어들에게 밀려 거의 없다. 사실 과거엔 컴파일러가 그야말로 발적화에 가까웠기 때문에[* 어느 정도로 발적화였냐면, while(1){ ... } 이 for(;;){ ... } 보다 느린 컴파일러가 많았다. 오늘날 컴파일러는 둘다 무한루프인 걸 알기 때문에 속도가 같지만, 과거 컴파일러의 경우에는 while 문의 경우 루프를 돌 때마다 항상 저 1 을 테스트했기 때문에 속도가 떨어졌다. 때문에, 연식이 좀 된 프로그래머들의 경우 지금도 습관적으로 while(1) 보다 for(;;) 를 애용하는 경우를 종종 볼 수 있다.] 어셈블리어를 이용하여 컴파일 된 코드를 고치거나 아예 어셈블리어로 프로그램을 짜기도 했지만 요즘은 PC 성능도 좋고 컴파일러도 좋아서 괜히 어셈블리어로 삽질할 필요는 거의 없다. 요즘은 오히려 컴파일러로 생성한 코드가 사람이 직접 어셈블리어로 작성한 코드보다 최적화가 잘 된 경우가 비일비재하다. 오래된 CPU와 달리 명령어 실행 시간이 조건에 따라 달라지기 때문에 사람이 그걸 다 따져가며 작업하기가 그만큼 힘들기 때문이다. 물론 컴파일러로는 절대로 할 수 없는 유형의 최적화도 존재하기는 한다. 예를 들어 하드 리얼 타임 시스템의 타이밍을 나노초 이하의 정밀도로 맞추는 최적화는 컴파일러로는 안 된다. 즉, 컴파일러보다 잘할 자신이 없다면 사용하는 의미가 없다. 그게 안 된다면 그냥 깔끔하게 C나 C++를 쓰자.[* 같은 맥락으로 C, C++ 코드 최적화에 서투른 사람이 짠 스파게티 코드Go, Java, JavaScript 등 추상화가 더욱 잘 되어있는 언어상에서 최적화가 잘 된 채로 짜인 같은 기능을 하는 코드에 비해서 더 느릴 수도 있다.] 물론 그게 가능한 소수의 능력자들이 있으며, 그들은 후술할 컴파일러, 운영체제, 임베디드 시스템 (드라이버 등) 등의 개발에 필수적이기에 최고급 기술자로서 엄청난 몸값을 받는다.

CPU 아키텍처가 바뀌면 이식이 잘 안 된다. 롤러코스터 타이쿤을 제작했던 크리스 소이어가 모바일 이식에 어려움을 겪고 있다고 말했다. 이유는 게임을 어셈블리어로만 만들었기 때문이다. [[1]]

생산성

CPU 성능이 낮고, RAM이 작고, 컴파일러보다 인간이 최적화를 더 잘 해야 하는 경우 (time-critical한 경우 등) 어셈블리어를 사용하는 것이 유리하다. 하지만 이런 환경이라 하더라도 CPU 성능이나 RAM이 조금만 확보되면 C, C++ 등의 고급 언어를 사용하는 것이 더 유리할 수 있다. 따라서 프로그래머는 둘 사이의 트레이드-오프를 적절하게 판단하여 프로젝트의 방향을 잡게 된다. 실제로 임베디드 시스템에서는 어셈블리어와 C/C++이 둘 다 쓰인다.

임베디드에서는 단가나 보드/칩 크기 문제로 8비트 CPU에 고작 램 몇 KB 정도만 쓰는 경우[* BSS 참조. 그리고 ATTiny의 램은 1KB 이하다.]도 많다. C 컴파일러에서는 함수호출 프로시저 등에서 사용되지 않는 코드를 기본적으로 생성하는 경우가 있다. 이 경우 RAM이 극히 작다면 불필요하게 차지하는 램 용량도 성능저하를 일으킬 수 있기 때문에 어셈블리어가 유리할 수 있다.

time-critical한 시스템에서는 타이밍을 맞추기 위해 어셈블리어의 사용이 더 많이 고려된다. 대표적으로 유도 미사일. 유도 미사일은 정해진 시간 안에 방향 계산을 완료하지 못하면 목표물을 격파시킬 수 없다. 어셈블리로 하지 않으면 타이밍을 맞추기 힘든 경우도 많다.

컴퓨터의 성능 자체가 낮았던 8비트 시절까지는 어셈블리어가 필수로 여겨졌다. 1950~1980년대에는 CPU의 구조가 매우 단순했고 RAM도 작았다. 거기다 이런 시스템을 활용하는 컴파일러의 최적화 성능도 나빴다. 그리고 소스코드의 분량이 짧아 어셈블리어로 개발하더라도 개발자들이 감당할 수 있을 수준이었다. 특히 게임의 경우 대부분 어셈블리어로 제작되었다. 파스칼, C 같은 고급 언어로 최대한의 퍼포먼스를 요하는 액션이나 슈팅 게임을 만든다는 것은 상상하기 어려웠다. 16비트 IBM PC에서 굴러가던 MS와 볼랜드의 컴파일러는 8비트 컴퓨터용으로도 있었지만, 게임용으로는 별로 쓰이지 않았다.[* 고급언어로 짠 게임 중 유명한 것은 울티마 I같은 RPG. 특히 8비트 애플용 화면은 보잘것없었기 때문에 베이직으로 짠 프로그램이 여럿 나왔다. MSX용 상업용 프로그램은 어셈블리어.] 90년대까지도 저사양 PC에서 처리속도를 올릴 목적으로 어셈블리어 코딩이 이루어졌다. 콘솔이 저성능이던 때에 어떻게든 게임 퍼포먼스를 쥐어짜 보려고 갖은 수를 쓰던 시절, 컴파일한 바이너리나 실행코드의 메모리 주소를 포인터로 찍고 기계어로 출력한 후 이것을 역어셈블한 결과물을 베이스로 작업하는 식이었다.[* 어셈블리어는 기계어와 1:1 대응이 되기 때문에 바이너리 ↔ 어셈블리어 양방향 전환이 자유로운 편이다.] 90년대에 들어 차츰 게임의 스케일이 커지고 컴퓨터의 구조가 복잡해지며 컴파일러의 최적화 성능도 개선되고 난 뒤에야 C 등의 고급언어로 이행했다.

단순한 연산을 무척이나 많이 반복하는 분야 - 예를 들면 Computer Vision - 에도 생각보다 많은 어셈블리어가 쓰인다. SIMD가 대표적이다. GPGPU를 사용하기에는 환경적으로 어려운 경우 - 가령, 모바일이나 GPU 성능이 좋지 못한 하드웨어에 해당 알고리즘들을 포팅해야 할 때 - 에는 특효약이 될 수 있다. OpenMP라든가, 일반적으로 빌드 시 컴파일러 옵션에서 SIMD 관련된 것들이 있지만, 이는 매우 제한적으로 적용되기 때문에 아직은 직접 사용자가 SIMD를 구현해야 한다. 다만, 어셈블리어를 직접 사용할 수도 있고, Intrinsics을 통해 좀 더 편한(?) C/C++ 스타일로 알고리즘을 구현할 수도 있다. 둘 간의 성능 차이도 생각보다 그렇게 크진 않다.

위와 같은 경우는 오늘날에는 특수한 경우가 되었다. 요즘 PC나 휴대폰에서 사용되는 일반적인 프로그램은 그냥 고급 언어를 사용한다. 전체적인 하드웨어의 성능이 대폭 상승하여 하나의 기기에 대한 최적화보다 다양한 기기에 대한 이식성, 생산성이 더 중요한 시대가 되었기 때문이다. 어셈블리어로 컴파일러를 이겨보겠다고 덤비는 그 시간과 인건비를 감수하는 것보다 그냥 한 스펙 더 높은 하드웨어를 사는 게 수백 수천 수억배(농담이 아니다) 싸게 먹힌다. 어셈블리를 대대적으로 동원해야 할 정도로 최적화가 절실할 경우에는 아예 FPGA, ASIC 등 전용 반도체를 제조해서 전용 머신을 만드는 게 싸다. 구글의 TPU가 이의 좋은 예이다. 소프트웨어로 아무리 빠르게 해 봐야 트랜지스터 게이트 레벨에서 최적화를 해 버린 주문형 반도체의 퍼포먼스를 따라잡을 수는 없다.

어셈블리어도 여러 가지가 존재하는데, 요즘 나오는 어셈블리어는 꽤 고수준 명령들을 지원하는 것들이 많고, 심지어 C에 가까운 프로그래밍이 가능한 것도 있다. 물론, 그만한 고수준 명령을 남발하면 퍼포먼스도 C만큼 떨어진다.~~등가교환~~ 다만, 어셈블리어라는 타이틀을 위해서는 기계어와 1:1 대응이 되어야 하기 때문에, 퍼포먼스를 원할 경우 의식적으로 그런 고수준 명령들을 배제하고 쓰면 상관이 없다. 이런 측면에서 보면 C보다 고수준 어셈블리어가 나을 수도 있지만, 어셈블리어는 결정적으로 명령어셋이 다른 플랫폼 간 호환성 문제가 절대 해결이 안 되는 치명적 단점이 있다.[* 물론, C의 호환성도 따지고 보면 미신이라고 반박하는 경우도 있는데 그래도 거의 완전히 갈아엎어야 하는 것과 부분부분 바꿔주면 되는 것의 차이는 크다.]

중요한 건, 어셈블리어를 사용한다고 꼭 퍼포먼스가 상승하는 것은 아니다. 크고 복잡한 프로그램들일수록 이런 경향이 있는데, 간단한 작업만 하기에도 복잡한 어셈블리어로 크고 복잡한 걸 만들어야 하는 경우 버그 없이 돌아가게 하는 것만 해도 이미 엄청난 일이 되는데, 여기에 일반적인 C 컴파일러가 해주는 최적화를 능가하는 수준의 최적화까지 하기란 웬만한 편집증이 아니고서야 사실 거의 불가능하다. 실제로, 역사가 좀 된 프로그램 중에는 과거 어셈블리어로 짰다가 C 컴파일러들이 충분히 발달한 이후, C 언어로 다시 만들고보니 퍼포먼스가 오히려 크게 상승했다는 경우도 종종 있는데 이는 C 컴파일러의 경우 최신 CPU 실행환경에 최적화된 바이너리 코드를 만드는 반면, 수제 어셈블리어 코드는 십수년 전 기준의 CPU 구조에 묶여 융통성 없는 바이너리 코드를 만들기 때문.

즉, 오늘날 임베디드 프로그래밍이 아닌 꽤 크고 복잡한 어플리케이션 프로그램에서 어셈블리어를 사용한다면, 보통 고수준 언어들로 만들어 놓은 다음 퍼포먼스 툴로 병목이 일어나는 부분을 찾아서 해당 코드만을 obj 파일 분석을 통해 어셈블리어로 튜닝을 해 주는 정도이다.[* 25년도 지난 이야기지만 볼랜드사의 터보C, 터보파스칼 컴파일러가 인라인 어셈블리를 쓸 수 있게 된 다음에 좋아한 개발자가 많았다.] 실제로 보통 프로그램에서 95%의 코드는 런타임 시간의 5%만 차지한다는 격언이 있다. 런타임의 95%를 차지하는 5% 정도의 핵심 코드를 찾아서 최적화를 해주는 게 코드 최적화의 핵심이라는 것이다. 그렇기에 성능에 거의 혹은 전혀 영향을 안 미치는 부분까지도 포함해서 전부 어셈블리어로 바닥부터 만드는 건 일종의 바보짓이 된다. ~~물론 해당 프로젝트에 인생을 바칠 각오가 되어있다면 상관없다.~~ CPU 스펙이 향상되었을 때 고급언어는 컴파일 명령 한 줄 내리는 것 외에 다른 건 아무 것도 할 필요가 없다는 점도 명심하자. 어셈블리어는 아키텍처는 고사하고 리비전만 올라가도 소스 코드를 아주 많이 수정해야 한다.

여담

* 어셈블리어를 기계어로 바꿔주는 프로그램을 어셈블러라고 한다.[* 컴파일러는 말 그대로 편집, 또는 수집을 한다는 소리이다. 예를 들어, C언어의 컴파일러는 소스 코드를 목적 코드로 바꾸고, 오브젝트 파일로 바꾼 후, 여러 컴파일에 필요한 파일을 모은 후에 바이너리(실행 파일)로 만들어 출력을 한다. 어셈블리어를 기계어로 바꾸는 과정에는 그런 거 없다. 그러기에 어셈블러와 컴파일러로 나누는 것이다.]
* 현대의 고성능 CPU아키텍처는 대부분 RISC방식의 어셈블리 명령셋을 지원하고 있다. RISC와 대비되는 CISC방식은 좀 더 사람에 친화적이고 RISC는 좀 더 기계에 친화적이다. 즉 현대의 CPU제조사들은 이미 어셈블리 언어를 사람이 직접 써서 코딩하는 상황을 전혀 고려하고 있지 않다.
* 가상머신에도 어셈블리어가 사용된다. Java바이트코드.NET CLR의 CIL이 대표적인 가상머신 어셈블리어. 다만, 가상머신 어셈블리어는 다양한 자료형을 지원하거나 함수를 지원하는 등 실제 어셈블리어와는 차이를 보이며, 대개 JIT 컴파일러를 거치거나 인터프리터를 통해 해석된다.
* 존 폰 노이만의 제자가 어셈블리어를 만들자 폰 노이만이 노발대발 했다는 이야기가 있다. 고작 그런 거로 컴퓨터님(…)의 성능을 낭비하려 한다나 뭐라나. 물론 이 당시 컴퓨터의 위상은 요즘 기준 수십억원대 최고급 연산장치였다.[* 그 당시가격으로 50만달러인데 지금 가치로는 6백만달러. 72억원정도이다. 애니악 한대가 그 가격.] 그렇기 때문에 국가 단위로 한, 두개 있는 정도였기 때문에 성능을 낭비한다는 것은 있을 수 없는 일. 물론 폰 노이만은 기계어로 자유자재로 코딩이 가능한 괴수였다는 점도 있다.~~그냥 그 머리로 직접 계산을 하시지 그러셨어요..라지만 저 인간은 진짜로 컴퓨터보다 암산이 빨랐던 괴수였다.~~ 세월이 지나고 심하게 비효율적인 코드도 높은 성능의 하드웨어와 컴파일러의 효과로 준수한 성능을 내기 때문에, 컴퓨터 한 대가 프로그래머 한 달 월급보다 싸진 요즘은 프로그래머의 효율이 훨씬 중요하게 취급된다.
* 크리스 소이어트랜스포트 타이쿤롤러코스터 타이쿤 1, 2까지도 이 언어로 짜여 있다. 엄청난 괴수임이 분명하다(…). 그래서 오브젝트가 난무하는 시나리오 후반부에도 음악파일을 가져올 때를 제외하고 랙이 거의 존재하지 않을 정도로 최적화가 잘 되어있다.[* 물론 아무리 어셈블리어로 짜도 못짜는 사람은 더럽게 랙 걸린다.~~물론 어셈블리어를 다룰줄 아는 사람이면 그만큼 괴수니까 그럴 가능성은 낮지만.~~ 여하튼 크리스 소이어의 실력이 대단하다는걸 알 수 있다.] 또한 저사양에서도 무난하게 돌아가기 때문에 전세계적으로 많은 사랑을 받을 수 있는 이유 중의 하나가 되었다.[* 트랜스포트 타이쿤은 386DX에서도 돌아간다!] 대신에 이거 때문에 모바일 게임으로 롤러코스터 타이쿤 1,2를 만들 때에는 크리스 소이어가 포팅에서 고생깨나 했다. 그리고 현재 테드 존(IntelOrca)이라는 영국 버크셔주 출신 프로그래머가 롤코타를 OpenRCT2로 오픈소스로 배포하기위해 리버스 엔지니어링을 한뒤 C 언어로 디컴파일을해서 만들었다고한다.정말 전무후무하다.
* 아타리 쇼크 이전에는 게임 프로그래밍이 곧 어셈블리어 프로그래밍이고 시스템 프로그래밍이었던 시절인지라 어떤 게임기이든 닥치고 어셈블리어로 작성할 수밖에 없었다. 슈퍼패미콤, 메가드라이브, 패미콤, MSX 등 8, 16비트 세대에 나온 대부분의 게임은 거의가 다 어셈블리어로 개발되었다. 게임보이까지 어셈블리어로 개발되어 포켓몬스터 2세대 게임까지가 어셈블리어 세대다.(...) 몇몇은 자체 언어를 제작하는 등 예외를 두고 있기는 하지만. 당시의 하드웨어 성능으로는 아무래도 어셈블리어로 직접 쓴 코드와 컴파일러가 생성하는 코드의 퍼포먼스 차이가 꽤 컸기 때문이다. 이후 컴파일러 성능이 좋아져서 요즘은 굳이 어셈블리어로 삽질할 필요가 없어졌다.[* MS의 매크로 어셈블러와 베이직 인터프리터는 IBM PC와 함께, 그리고 볼랜드의 터보 시리즈 컴파일러가 8비트 컴퓨터용으로 출시됐지만, 당시 컴퓨터책과 잡지에서는 C언어를 저급언어라 부르면서도 어셈블리어 강좌를 연재했고, 개인프로그래머는 변환테이블을 보고 핸드어셈블한 걸 기계어 모니터 프로그램으로 직접 입력하기도 했다. 디버그는 트레이스(TRON, TROFF)를 이용해 레지스터와 포인터, 플래그를 직접 보며 근성으로!] 그만큼 게임기의 성능이 좋아지고 게임의 용량이 커졌다는 뜻이다. 대신에 게임에 고급언어가 들어가며 발적화 문제가 대두되기 시작했다.(...) ~~그냥 다시 어셈블리어 게임 시대로 돌아가야.. 물론 생산성이 막장이라 수십 배의 코드가 필요하다.~~
* 1980년대 컴퓨터 잡지(예를 들어 일본 I/O 지)를 보면 프로그램의 어셈블리어 코드가 빽빽하게 인쇄된 페이지가 수백장씩 있었다. 독자는 그걸 한글자 한글자 직접 입력해서 프로그램을 복사...?하곤 했다. 물론 당시에도 카세트 테입 등의 저장매체가 있었으니 그런 것에 프로그램을 담아 배포할 수도 있었을 터이나, 이런 잡지의 주목적은 프로그래밍 언어의 학습이었으므로 코드를 직접 입력하며 그 내용을 공부하라는 의미가 있었고, 애초에 카세트 테이프를 읽을 수 있는 데이터 레코더마저도 갖추지 않은 컴퓨터도 많은데다, 지금은 거저 줘도 쓰지 않을 카세트 테이프도 당시 기준으로는 부록이라고 무료로 마음껏 배포할 수 있는 자원도 아니었다.[* 기본적으로 카세트 테이프는 CD보다 제작 단가가 높다. 90년대 초반까지만 해도 조금이라도 돈을 더 아끼려고 오래된 어학 테이프 등을 지우고 음악을 녹음해서 담고 다니던 시절이 있었다.] 인쇄물로 나눠주는 것이 가장 확실한 배포방법이기도 했던 셈. 여러모로 지금에 와서는 상상하기 힘든 일이다. 한 바이트 틀리면 폭주!
* 안철수가 만든 V3 백신의 토대인 V1은 어셈블리어로 개발되어 있다.[* [[2]]에서 발췌][* 다만 이진 파일 속에서 바이러스를 찾는다는게 딱히 기계어나 어셈블리어와 상관이 있는 건 아니다. 이런 경우는 바이러스가 파일 내부를 건드리는 부분만 잡아서 검출만 할 수 있으면 되기 때문에 파일 자체의 구조 분석이 더 자주 쓰인다. 그 시대 프로그램들이 다 그랬듯이 V1도 그 흐름을 따라서 어셈블리어로 개발되었을 뿐이다.]
* 어셈블리 코드를 짜는 시뮬레이션 게임이 스팀에 판매중이다... TIS-100. 너무 어렵게 생각한다면 Human Resource Machine을 해보는 것도 도움이 될것이다.
* 바람의 나라 (1996)는 어셈블리어로 코딩되었다. 6.30 버전 (2004) 이후로 DirectX를 지원하는 버전으로 모두 교체되었다.

분류:프로그래밍 언어